Merge branch 'master' into ben/stacks
This commit is contained in:
commit
6b0f18a91f
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
|
@ -92,6 +92,21 @@
|
|||
"request": "launch",
|
||||
"program": "${workspaceRoot}/src/vs/languages/css/common/buildscripts/generate_browserjs.js",
|
||||
"stopOnEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Debug monaco",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/lib/monaco.js",
|
||||
"stopOnEntry": false,
|
||||
"args": [
|
||||
],
|
||||
"cwd": "${workspaceRoot}/build/lib"
|
||||
// ,
|
||||
|
||||
// "port": 5870,
|
||||
// "sourceMaps": true,
|
||||
// "outDir": "${workspaceRoot}/out"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -40,10 +40,10 @@ exports.loaderConfig = function (emptyPaths) {
|
|||
paths: {
|
||||
'vs/extensions': 'extensions'
|
||||
}
|
||||
}
|
||||
},
|
||||
nodeModules: emptyPaths||[]
|
||||
};
|
||||
|
||||
(emptyPaths || []).forEach(function(m) { result.paths[m] = 'empty:'; });
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,13 +31,12 @@ var languages = ['chs', 'cht', 'jpn', 'kor', 'deu', 'fra', 'esn', 'rus', 'ita'];
|
|||
|
||||
var tasks = compilations.map(function(tsconfigFile) {
|
||||
var absolutePath = path.join(extensionsPath, tsconfigFile);
|
||||
var absoluteDirname = path.dirname(absolutePath);
|
||||
var relativeDirname = path.dirname(tsconfigFile);
|
||||
|
||||
var tsOptions = require(absolutePath).compilerOptions;
|
||||
tsOptions.verbose = !quiet;
|
||||
tsOptions.sourceMap = true;
|
||||
tsOptions.sourceRoot = util.toFileUri(path.join(absoluteDirname, 'src'));
|
||||
tsOptions.sourceRoot = '../src';
|
||||
|
||||
var name = relativeDirname.replace(/\//g, '-');
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ var baseModules = [
|
|||
'applicationinsights', 'assert', 'child_process', 'chokidar', 'crypto', 'emmet',
|
||||
'events', 'fs', 'getmac', 'glob', 'graceful-fs', 'http', 'http-proxy-agent',
|
||||
'https', 'https-proxy-agent', 'iconv-lite', 'electron', 'net',
|
||||
'os', 'path', 'readline', 'sax', 'semver', 'stream', 'string_decoder', 'url',
|
||||
'os', 'path', 'pty.js', 'readline', 'sax', 'semver', 'stream', 'string_decoder', 'url', 'term.js',
|
||||
'vscode-textmate', 'winreg', 'yauzl', 'native-keymap', 'zlib', 'minimist'
|
||||
];
|
||||
|
||||
|
@ -52,6 +52,7 @@ var vscodeResources = [
|
|||
'out-build/cli.js',
|
||||
'out-build/bootstrap.js',
|
||||
'out-build/bootstrap-amd.js',
|
||||
'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',
|
||||
|
@ -67,6 +68,7 @@ var vscodeResources = [
|
|||
'out-build/vs/workbench/parts/html/browser/webview.html',
|
||||
'out-build/vs/workbench/parts/markdown/**/*.md',
|
||||
'out-build/vs/workbench/parts/tasks/**/*.json',
|
||||
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',
|
||||
'out-build/vs/workbench/services/files/**/*.exe',
|
||||
'out-build/vs/workbench/services/files/**/*.md',
|
||||
'!**/test/**'
|
||||
|
@ -295,6 +297,10 @@ function prepareDebPackage(arch) {
|
|||
.pipe(replace('@@NAME@@', product.applicationName))
|
||||
.pipe(rename('DEBIAN/prerm'))
|
||||
|
||||
var postrm = gulp.src('resources/linux/debian/postrm.template', { base: '.' })
|
||||
.pipe(replace('@@NAME@@', product.applicationName))
|
||||
.pipe(rename('DEBIAN/postrm'))
|
||||
|
||||
var postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' })
|
||||
.pipe(replace('@@NAME@@', product.applicationName))
|
||||
.pipe(replace('@@ARCHITECTURE@@', debArch))
|
||||
|
@ -302,7 +308,7 @@ function prepareDebPackage(arch) {
|
|||
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
|
||||
.pipe(rename('DEBIAN/postinst'))
|
||||
|
||||
var all = es.merge(control, postinst, prerm, desktop, icon, code);
|
||||
var all = es.merge(control, postinst, postrm, prerm, desktop, icon, code);
|
||||
|
||||
return all.pipe(symdest(destination));
|
||||
};
|
||||
|
|
4252
build/lib/monaco-editor.d.ts
vendored
Normal file
4252
build/lib/monaco-editor.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
57
build/lib/monaco-editor.d.ts.recipe
Normal file
57
build/lib/monaco-editor.d.ts.recipe
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
declare module monaco {
|
||||
|
||||
interface Thenable<R> {
|
||||
/**
|
||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||
* @param onfulfilled The callback to execute when the Promise is resolved.
|
||||
* @param onrejected The callback to execute when the Promise is rejected.
|
||||
* @returns A Promise for the completion of which ever callback is executed.
|
||||
*/
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||
}
|
||||
|
||||
export interface Event<T> {
|
||||
(listener: (e: T) => any): IDisposable;
|
||||
}
|
||||
|
||||
export class Emitter<T> {
|
||||
constructor();
|
||||
event: Event<T>;
|
||||
fire(event?: T): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
#include(vs/base/common/uri): URI
|
||||
|
||||
#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, TPromise
|
||||
|
||||
}
|
||||
|
||||
declare module monaco.editor {
|
||||
|
||||
export function create(domElement: HTMLElement, options: IEditorConstructionOptions, services?: any): ICodeEditor;
|
||||
export function createDiffEditor(domElement: HTMLElement, options: IDiffEditorConstructionOptions, services?: any): IDiffEditor;
|
||||
export function createModel(value:string, mode:string|IMonarchLanguage|IMode, associatedResource?:any|string): IModel;
|
||||
export function getOrCreateMode(modeId: string): TPromise<IMode>;
|
||||
export function createCustomMode(description:IMonarchLanguage): TPromise<IMode>;
|
||||
export function colorize(text: string, modeId: string, options: IColorizerOptions): TPromise<string>;
|
||||
export function colorizeElement(domNode: HTMLElement, options: IColorizerElementOptions): TPromise<void>;
|
||||
export function colorizeLine(line: string, tokens: Editor.ILineToken[], tabSize?: number): string;
|
||||
export function colorizeModelLine(model: Editor.IModel, lineNumber: number, tabSize?: number): string;
|
||||
export function registerWorkerParticipant(modeId:string, moduleName:string, ctorName:string): void;
|
||||
export function configureMode(modeId: string, options: any): void;
|
||||
|
||||
#include(vs/base/browser/ui/scrollbar/scrollableElementOptions): ScrollbarVisibility
|
||||
#include(vs/base/common/htmlContent): IHTMLContentElementCode, IHTMLContentElement
|
||||
#include(vs/editor/common/modes): ILineContext, IMode, IModeTransition, IToken
|
||||
|
||||
#includeAll(vs/editor/common/editorCommon):
|
||||
#includeAll(vs/editor/browser/editorBrowser):
|
||||
|
||||
}
|
117
build/lib/monaco.js
Normal file
117
build/lib/monaco.js
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
"use strict";
|
||||
// PREREQUISITE:
|
||||
// SET VSCODE_BUILD_DECLARATION_FILES=1
|
||||
// run gulp watch once
|
||||
var fs = require('fs');
|
||||
var ts = require('typescript');
|
||||
var path = require('path');
|
||||
var SRC = path.join(__dirname, '../../src');
|
||||
var OUT = path.join(__dirname, '../../out');
|
||||
function moduleIdToPath(moduleId) {
|
||||
if (/\.d\.ts/.test(moduleId)) {
|
||||
return path.join(SRC, moduleId);
|
||||
}
|
||||
return path.join(OUT, moduleId) + '.d.ts';
|
||||
}
|
||||
var SOURCE_FILE_MAP = {};
|
||||
function getSourceFile(moduleId) {
|
||||
if (!SOURCE_FILE_MAP[moduleId]) {
|
||||
var filePath = moduleIdToPath(moduleId);
|
||||
var fileContents = fs.readFileSync(filePath).toString();
|
||||
var sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5);
|
||||
SOURCE_FILE_MAP[moduleId] = sourceFile;
|
||||
}
|
||||
return SOURCE_FILE_MAP[moduleId];
|
||||
}
|
||||
function visitTopLevelDeclarations(sourceFile, visitor) {
|
||||
var stop = false;
|
||||
var visit = function (node) {
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.InterfaceDeclaration:
|
||||
case ts.SyntaxKind.EnumDeclaration:
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
stop = visitor(node);
|
||||
break;
|
||||
}
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
visit(sourceFile);
|
||||
}
|
||||
function getAllTopLevelDeclarations(sourceFile) {
|
||||
var all = [];
|
||||
visitTopLevelDeclarations(sourceFile, function (node) {
|
||||
all.push(node);
|
||||
return false /*continue*/;
|
||||
});
|
||||
return all;
|
||||
}
|
||||
function getTopLevelDeclaration(sourceFile, typeName) {
|
||||
var result = null;
|
||||
visitTopLevelDeclarations(sourceFile, function (node) {
|
||||
if (node.name.text === typeName) {
|
||||
result = node;
|
||||
return true /*stop*/;
|
||||
}
|
||||
return false /*continue*/;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function getNodeText(sourceFile, node) {
|
||||
return sourceFile.getFullText().substring(node.pos, node.end);
|
||||
}
|
||||
function getMassagedTopLevelDeclarationText(sourceFile, declaration) {
|
||||
var result = getNodeText(sourceFile, declaration);
|
||||
result = result.replace(/export default/g, 'export');
|
||||
result = result.replace(/export declare/g, 'export');
|
||||
return result;
|
||||
}
|
||||
var recipe = fs.readFileSync(path.join(__dirname, './monaco-editor.d.ts.recipe')).toString();
|
||||
var lines = recipe.split(/\r\n|\n|\r/);
|
||||
var result = [];
|
||||
lines.forEach(function (line) {
|
||||
var m1 = line.match(/^\s*#include\(([^\)]*)\)\:(.*)$/);
|
||||
if (m1) {
|
||||
var moduleId = m1[1];
|
||||
var sourceFile_1 = getSourceFile(moduleId);
|
||||
var typeNames = m1[2].split(/,/);
|
||||
typeNames.forEach(function (typeName) {
|
||||
typeName = typeName.trim();
|
||||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
var declaration = getTopLevelDeclaration(sourceFile_1, typeName);
|
||||
result.push(getMassagedTopLevelDeclarationText(sourceFile_1, declaration));
|
||||
});
|
||||
return;
|
||||
}
|
||||
var m2 = line.match(/^\s*#includeAll\(([^\)]*)\)\:(.*)$/);
|
||||
if (m2) {
|
||||
var moduleId = m2[1];
|
||||
var sourceFile_2 = getSourceFile(moduleId);
|
||||
var typeNames = m2[2].split(/,/);
|
||||
var typesToExclude_1 = {};
|
||||
typeNames.forEach(function (typeName) {
|
||||
typeName = typeName.trim();
|
||||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
typesToExclude_1[typeName] = true;
|
||||
});
|
||||
getAllTopLevelDeclarations(sourceFile_2).forEach(function (declaration) {
|
||||
result.push(getMassagedTopLevelDeclarationText(sourceFile_2, declaration));
|
||||
});
|
||||
return;
|
||||
}
|
||||
result.push(line);
|
||||
});
|
||||
fs.writeFileSync(path.join(__dirname, './monaco-editor.d.ts'), result.join('\n'));
|
153
build/lib/monaco.ts
Normal file
153
build/lib/monaco.ts
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// PREREQUISITE:
|
||||
// SET VSCODE_BUILD_DECLARATION_FILES=1
|
||||
// run gulp watch once
|
||||
|
||||
|
||||
import fs = require('fs');
|
||||
import ts = require('typescript');
|
||||
import path = require('path');
|
||||
|
||||
|
||||
const SRC = path.join(__dirname, '../../src');
|
||||
const OUT = path.join(__dirname, '../../out');
|
||||
|
||||
|
||||
function moduleIdToPath(moduleId:string): string {
|
||||
if (/\.d\.ts/.test(moduleId)) {
|
||||
return path.join(SRC, moduleId);
|
||||
}
|
||||
return path.join(OUT, moduleId) + '.d.ts';
|
||||
}
|
||||
|
||||
|
||||
var SOURCE_FILE_MAP: {[moduleId:string]:ts.SourceFile;} = {};
|
||||
function getSourceFile(moduleId:string): ts.SourceFile {
|
||||
if (!SOURCE_FILE_MAP[moduleId]) {
|
||||
let filePath = moduleIdToPath(moduleId);
|
||||
let fileContents = fs.readFileSync(filePath).toString();
|
||||
let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5);
|
||||
|
||||
SOURCE_FILE_MAP[moduleId] = sourceFile;
|
||||
}
|
||||
return SOURCE_FILE_MAP[moduleId];
|
||||
}
|
||||
|
||||
|
||||
type TypeScriptTypeDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration;
|
||||
|
||||
|
||||
function visitTopLevelDeclarations(sourceFile:ts.SourceFile, visitor:(node:TypeScriptTypeDeclaration)=>boolean): void {
|
||||
let stop = false;
|
||||
|
||||
let visit = (node: ts.Node): void => {
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.InterfaceDeclaration:
|
||||
case ts.SyntaxKind.EnumDeclaration:
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
stop = visitor(<TypeScriptTypeDeclaration>node);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
|
||||
visit(sourceFile);
|
||||
}
|
||||
|
||||
|
||||
function getAllTopLevelDeclarations(sourceFile:ts.SourceFile): TypeScriptTypeDeclaration[] {
|
||||
let all:TypeScriptTypeDeclaration[] = [];
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
all.push(node);
|
||||
return false /*continue*/;
|
||||
});
|
||||
return all;
|
||||
}
|
||||
|
||||
|
||||
function getTopLevelDeclaration(sourceFile:ts.SourceFile, typeName:string): TypeScriptTypeDeclaration {
|
||||
let result:TypeScriptTypeDeclaration = null;
|
||||
visitTopLevelDeclarations(sourceFile, (node) => {
|
||||
if (node.name.text === typeName) {
|
||||
result = node;
|
||||
return true /*stop*/;
|
||||
}
|
||||
return false /*continue*/;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function getNodeText(sourceFile:ts.SourceFile, node:ts.Node): string {
|
||||
return sourceFile.getFullText().substring(node.pos, node.end);
|
||||
}
|
||||
|
||||
|
||||
function getMassagedTopLevelDeclarationText(sourceFile:ts.SourceFile, declaration: TypeScriptTypeDeclaration): string {
|
||||
let result = getNodeText(sourceFile, declaration);
|
||||
result = result.replace(/export default/g, 'export');
|
||||
result = result.replace(/export declare/g, 'export');
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
var recipe = fs.readFileSync(path.join(__dirname, './monaco-editor.d.ts.recipe')).toString();
|
||||
var lines = recipe.split(/\r\n|\n|\r/);
|
||||
var result = [];
|
||||
|
||||
lines.forEach(line => {
|
||||
|
||||
let m1 = line.match(/^\s*#include\(([^\)]*)\)\:(.*)$/);
|
||||
if (m1) {
|
||||
let moduleId = m1[1];
|
||||
let sourceFile = getSourceFile(moduleId);
|
||||
|
||||
let typeNames = m1[2].split(/,/);
|
||||
typeNames.forEach((typeName) => {
|
||||
typeName = typeName.trim();
|
||||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
let declaration = getTopLevelDeclaration(sourceFile, typeName);
|
||||
result.push(getMassagedTopLevelDeclarationText(sourceFile, declaration));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let m2 = line.match(/^\s*#includeAll\(([^\)]*)\)\:(.*)$/);
|
||||
if (m2) {
|
||||
let moduleId = m2[1];
|
||||
let sourceFile = getSourceFile(moduleId);
|
||||
|
||||
let typeNames = m2[2].split(/,/);
|
||||
let typesToExclude: {[typeName:string]:boolean;} = {};
|
||||
typeNames.forEach((typeName) => {
|
||||
typeName = typeName.trim();
|
||||
if (typeName.length === 0) {
|
||||
return;
|
||||
}
|
||||
typesToExclude[typeName] = true;
|
||||
});
|
||||
|
||||
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
|
||||
result.push(getMassagedTopLevelDeclarationText(sourceFile, declaration));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
result.push(line);
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, './monaco-editor.d.ts'), result.join('\n'));
|
|
@ -12,7 +12,7 @@
|
|||
},
|
||||
{
|
||||
"id": "cpp",
|
||||
"extensions": [ ".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx", ".h", ".mm", ".ino" ],
|
||||
"extensions": [ ".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx", ".h", ".mm", ".ino", ".inl" ],
|
||||
"aliases": [ "C++", "Cpp", "cpp"],
|
||||
"configuration": "./cpp.configuration.json"
|
||||
}],
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {Location, getLocation, createScanner, SyntaxKind} from 'jsonc-parser';
|
||||
import {Location, getLocation, createScanner, SyntaxKind, ScanError} from 'jsonc-parser';
|
||||
import {basename} from 'path';
|
||||
import {BowerJSONContribution} from './bowerJSONContribution';
|
||||
import {PackageJSONContribution} from './packageJSONContribution';
|
||||
|
@ -122,10 +122,7 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
|
|||
|
||||
if (location.isAtPropertyKey) {
|
||||
let addValue = !location.previousNode || !location.previousNode.columnOffset;
|
||||
let scanner = createScanner(document.getText(), true);
|
||||
scanner.setPosition(offset);
|
||||
scanner.scan();
|
||||
let isLast = scanner.getToken() === SyntaxKind.CloseBraceToken || scanner.getToken() === SyntaxKind.EOF;
|
||||
let isLast = this.isLast(document, position);
|
||||
collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector);
|
||||
} else {
|
||||
if (location.path.length === 0) {
|
||||
|
@ -153,4 +150,14 @@ export class JSONCompletionItemProvider implements CompletionItemProvider {
|
|||
}
|
||||
return text.substring(i+1, position.character);
|
||||
}
|
||||
|
||||
private isLast(document: TextDocument, position: Position):boolean {
|
||||
let scanner = createScanner(document.getText(), true);
|
||||
scanner.setPosition(document.offsetAt(position));
|
||||
let nextToken = scanner.scan();
|
||||
if (nextToken === SyntaxKind.StringLiteral && scanner.getTokenError() === ScanError.UnexpectedEndOfString) {
|
||||
nextToken= scanner.scan();
|
||||
}
|
||||
return nextToken === SyntaxKind.CloseBraceToken || nextToken === SyntaxKind.EOF;
|
||||
}
|
||||
}
|
|
@ -210,6 +210,12 @@ function updateConfiguration() {
|
|||
|
||||
|
||||
function validateTextDocument(textDocument: ITextDocument): void {
|
||||
if (textDocument.getText().length === 0) {
|
||||
// ignore empty documents
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonDocument = getJSONDocument(textDocument);
|
||||
jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(schema => {
|
||||
if (schema) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"account": "monacobuild",
|
||||
"container": "debuggers",
|
||||
"zip": "c2c41f3/node-debug.zip",
|
||||
"zip": "3c7ed19/node-debug.zip",
|
||||
"output": ""
|
||||
}
|
||||
|
|
|
@ -11,11 +11,16 @@
|
|||
"contributes": {
|
||||
"languages": [{
|
||||
"id": "php",
|
||||
"extensions": [ ".php", ".phtml", ".ctp" ],
|
||||
"extensions": [ ".php", ".php4", ".php5", ".phtml", ".ctp" ],
|
||||
"aliases": [ "PHP", "php" ],
|
||||
"mimetypes": ["application/x-php"],
|
||||
"configuration": "./php.configuration.json"
|
||||
}],
|
||||
"grammars": [{
|
||||
"language": "php",
|
||||
"scopeName": "text.html.php",
|
||||
"path": "./syntaxes/php.json"
|
||||
}],
|
||||
"snippets": [{
|
||||
"language": "php",
|
||||
"path": "./snippets/php.json"
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
["[", "]"],
|
||||
["(", ")"]
|
||||
]
|
||||
|
||||
}
|
2638
extensions/php/syntaxes/php.json
Normal file
2638
extensions/php/syntaxes/php.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -12,8 +12,7 @@
|
|||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
["\"", "\""]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
|
@ -22,4 +21,4 @@
|
|||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"name": "Microsoft Corp."
|
||||
},
|
||||
"homepage": "http://typescriptlang.org/",
|
||||
"version": "1.8.9",
|
||||
"version": "1.8.10",
|
||||
"license": "Apache-2.0",
|
||||
"description": "TypeScript is a language for application scale JavaScript development",
|
||||
"keywords": [
|
||||
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/TypeScript.git"
|
||||
"url": "git+https://github.com/Microsoft/TypeScript.git"
|
||||
},
|
||||
"main": "./lib/typescript.js",
|
||||
"typings": "./lib/typescript.d.ts",
|
||||
|
@ -58,11 +58,12 @@
|
|||
"os": false,
|
||||
"path": false
|
||||
},
|
||||
"gitHead": "9ef75534e0fd5f92bef86b520dff768c11a2df4d",
|
||||
"_id": "typescript@1.8.9",
|
||||
"_shasum": "b3b3a74059fd31cbd3ecad95d62465939e7ed5fa",
|
||||
"gitHead": "794c57478ec2a44ee15fb3e245a4c5d2d1612375",
|
||||
"_id": "typescript@1.8.10",
|
||||
"_shasum": "b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e",
|
||||
"_from": "typescript@latest",
|
||||
"_npmVersion": "2.0.0",
|
||||
"_npmVersion": "2.14.2",
|
||||
"_nodeVersion": "5.9.0",
|
||||
"_npmUser": {
|
||||
"name": "typescript",
|
||||
"email": "typescript@microsoft.com"
|
||||
|
@ -74,13 +75,14 @@
|
|||
}
|
||||
],
|
||||
"dist": {
|
||||
"shasum": "b3b3a74059fd31cbd3ecad95d62465939e7ed5fa",
|
||||
"tarball": "http://registry.npmjs.org/typescript/-/typescript-1.8.9.tgz"
|
||||
"shasum": "b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e",
|
||||
"tarball": "https://registry.npmjs.org/typescript/-/typescript-1.8.10.tgz"
|
||||
},
|
||||
"_npmOperationalInternal": {
|
||||
"host": "packages-12-west.internal.npmjs.com",
|
||||
"tmp": "tmp/typescript-1.8.9.tgz_1458169371557_0.7292261146940291"
|
||||
"tmp": "tmp/typescript-1.8.10.tgz_1460493736776_0.9304528103675693"
|
||||
},
|
||||
"directories": {},
|
||||
"_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.9.tgz"
|
||||
"_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.10.tgz",
|
||||
"readme": "ERROR: No README data found!"
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
|
|||
}
|
||||
}
|
||||
if (this.trace !== Trace.Off) {
|
||||
this.output.append(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.`);
|
||||
this.output.append(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.\n`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -40,23 +40,6 @@ suite('commands namespace tests', () => {
|
|||
}, done);
|
||||
});
|
||||
|
||||
test('api-command: workbench.html.preview', function () {
|
||||
|
||||
let registration = workspace.registerTextDocumentContentProvider('speciale', {
|
||||
provideTextDocumentContent(uri) {
|
||||
return `content of URI <b>${uri.toString()}</b>`;
|
||||
}
|
||||
});
|
||||
|
||||
let virtualDocumentUri = Uri.parse('speciale://authority/path');
|
||||
|
||||
return commands.executeCommand('vscode.previewHtml', virtualDocumentUri).then(success => {
|
||||
assert.ok(success);
|
||||
registration.dispose();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('editorCommand with extra args', function () {
|
||||
|
||||
let args: IArguments;
|
||||
|
@ -77,4 +60,46 @@ suite('commands namespace tests', () => {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
test('api-command: vscode.previewHtm', function () {
|
||||
|
||||
let registration = workspace.registerTextDocumentContentProvider('speciale', {
|
||||
provideTextDocumentContent(uri) {
|
||||
return `content of URI <b>${uri.toString()}</b>`;
|
||||
}
|
||||
});
|
||||
|
||||
let virtualDocumentUri = Uri.parse('speciale://authority/path');
|
||||
|
||||
return commands.executeCommand('vscode.previewHtml', virtualDocumentUri).then(success => {
|
||||
assert.ok(success);
|
||||
registration.dispose();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('api-command: vscode.diff', function () {
|
||||
|
||||
let registration = workspace.registerTextDocumentContentProvider('sc', {
|
||||
provideTextDocumentContent(uri) {
|
||||
return `content of URI <b>${uri.toString()}</b>#${Math.random()}`;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let a = commands.executeCommand('vscode.diff', Uri.parse('sc:a'), Uri.parse('sc:b'), 'DIFF').then(value => {
|
||||
assert.ok(value === void 0);
|
||||
registration.dispose();
|
||||
});
|
||||
|
||||
let b = commands.executeCommand('vscode.diff', Uri.parse('sc:a'), Uri.parse('sc:b')).then(value => {
|
||||
assert.ok(value === void 0);
|
||||
registration.dispose();
|
||||
});
|
||||
|
||||
let c = commands.executeCommand('vscode.diff').then(() => assert.ok(false), () => assert.ok(true));
|
||||
let d = commands.executeCommand('vscode.diff', 1, 2, 3).then(() => assert.ok(false), () => assert.ok(true));
|
||||
|
||||
return Promise.all([a, b, c]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import * as assert from 'assert';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import {workspace, window, Position} from 'vscode';
|
||||
import {workspace, window, Position, Range} from 'vscode';
|
||||
import {createRandomFile, deleteFile, cleanUp} from './utils';
|
||||
import {join} from 'path';
|
||||
|
||||
|
@ -38,4 +38,27 @@ suite("editor tests", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('issue #6281: Edits fail to validate ranges correctly before applying', () => {
|
||||
return createRandomFile('Hello world!').then(file => {
|
||||
return workspace.openTextDocument(file).then(doc => {
|
||||
return window.showTextDocument(doc).then((editor) => {
|
||||
return editor.edit((builder) => {
|
||||
builder.replace(new Range(0, 0, Number.MAX_VALUE, Number.MAX_VALUE), 'new');
|
||||
}).then(applied => {
|
||||
assert.ok(applied);
|
||||
assert.equal(doc.getText(), 'new');
|
||||
assert.ok(doc.isDirty);
|
||||
|
||||
return doc.save().then(saved => {
|
||||
assert.ok(saved);
|
||||
assert.ok(!doc.isDirty);
|
||||
|
||||
return deleteFile(file);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -79,6 +79,55 @@ suite('languages namespace tests', () => {
|
|||
collection.dispose();
|
||||
});
|
||||
|
||||
test('diagnostics collection, set with dupliclated tuples', function () {
|
||||
let collection = languages.createDiagnosticCollection('test');
|
||||
let uri = Uri.parse('sc:hightower');
|
||||
collection.set([
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
|
||||
[Uri.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]],
|
||||
]);
|
||||
|
||||
let array = collection.get(uri);
|
||||
assert.equal(array.length, 2);
|
||||
let [first, second] = array;
|
||||
assert.equal(first.message, 'message-1');
|
||||
assert.equal(second.message, 'message-2');
|
||||
|
||||
// clear
|
||||
collection.delete(uri);
|
||||
assert.ok(!collection.has(uri));
|
||||
|
||||
// bad tuple clears 1/2
|
||||
collection.set([
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
|
||||
[Uri.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
|
||||
[uri, undefined]
|
||||
]);
|
||||
assert.ok(!collection.has(uri));
|
||||
|
||||
// clear
|
||||
collection.delete(uri);
|
||||
assert.ok(!collection.has(uri));
|
||||
|
||||
// bad tuple clears 2/2
|
||||
collection.set([
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
|
||||
[Uri.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
|
||||
[uri, undefined],
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]],
|
||||
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-3')]],
|
||||
]);
|
||||
|
||||
array = collection.get(uri);
|
||||
assert.equal(array.length, 2);
|
||||
[first, second] = array;
|
||||
assert.equal(first.message, 'message-2');
|
||||
assert.equal(second.message, 'message-3');
|
||||
|
||||
collection.dispose();
|
||||
});
|
||||
|
||||
test('diagnostics & CodeActionProvider', function (done) {
|
||||
|
||||
class D2 extends Diagnostic {
|
||||
|
|
|
@ -294,6 +294,23 @@ suite('workspace-namespace', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('registerTextDocumentContentProvider, empty doc', function () {
|
||||
|
||||
let registration = workspace.registerTextDocumentContentProvider('foo', {
|
||||
provideTextDocumentContent(uri) {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
const uri = Uri.parse('foo:doc/empty');
|
||||
|
||||
return workspace.openTextDocument(uri).then(doc => {
|
||||
assert.equal(doc.getText(), '');
|
||||
assert.equal(doc.uri.toString(), uri.toString());
|
||||
registration.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
test('registerTextDocumentContentProvider, change event', function () {
|
||||
|
||||
let callCount = 0;
|
||||
|
|
|
@ -25,10 +25,12 @@ var sourcemaps = require('gulp-sourcemaps');
|
|||
var _ = require('underscore');
|
||||
var assign = require('object-assign');
|
||||
var quiet = !!process.env['VSCODE_BUILD_QUIET'];
|
||||
var declaration = !!process.env['VSCODE_BUILD_DECLARATION_FILES'];
|
||||
|
||||
var rootDir = path.join(__dirname, 'src');
|
||||
var tsOptions = {
|
||||
target: 'ES5',
|
||||
declaration: declaration,
|
||||
module: 'amd',
|
||||
verbose: !quiet,
|
||||
preserveConstEnums: true,
|
||||
|
|
16
npm-shrinkwrap.json
generated
16
npm-shrinkwrap.json
generated
|
@ -340,6 +340,11 @@
|
|||
"from": "preserve@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz"
|
||||
},
|
||||
"pty.js": {
|
||||
"version": "0.3.0",
|
||||
"from": "https://github.com/Tyriar/pty.js/tarball/fffbf86eb9e8051b5b2be4ba9c7b07faa018ce8d",
|
||||
"resolved": "https://github.com/Tyriar/pty.js/tarball/fffbf86eb9e8051b5b2be4ba9c7b07faa018ce8d"
|
||||
},
|
||||
"randomatic": {
|
||||
"version": "1.1.5",
|
||||
"from": "randomatic@>=1.1.3 <2.0.0",
|
||||
|
@ -390,15 +395,20 @@
|
|||
"from": "string_decoder@>=0.10.0 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
|
||||
},
|
||||
"term.js": {
|
||||
"version": "0.0.7",
|
||||
"from": "https://github.com/jeremyramin/term.js/tarball/master",
|
||||
"resolved": "https://github.com/jeremyramin/term.js/tarball/master"
|
||||
},
|
||||
"typechecker": {
|
||||
"version": "2.0.8",
|
||||
"from": "typechecker@>=2.0.1 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/typechecker/-/typechecker-2.0.8.tgz"
|
||||
},
|
||||
"vscode-debugprotocol": {
|
||||
"version": "1.8.0-pre.3",
|
||||
"from": "vscode-debugprotocol@1.8.0-pre.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.8.0-pre.3.tgz"
|
||||
"version": "1.8.0",
|
||||
"from": "vscode-debugprotocol@1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.8.0.tgz"
|
||||
},
|
||||
"vscode-textmate": {
|
||||
"version": "1.0.11",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Code",
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.1.0",
|
||||
"electronVersion": "0.37.6",
|
||||
"author": {
|
||||
|
@ -27,9 +27,11 @@
|
|||
"iconv-lite": "0.4.13",
|
||||
"minimist": "^1.2.0",
|
||||
"native-keymap": "0.1.2",
|
||||
"pty.js": "https://github.com/Tyriar/pty.js/tarball/prebuilt",
|
||||
"sax": "1.1.2",
|
||||
"semver": "4.3.6",
|
||||
"vscode-debugprotocol": "1.8.0-pre.3",
|
||||
"term.js": "https://github.com/jeremyramin/term.js/tarball/master",
|
||||
"vscode-debugprotocol": "1.8.0",
|
||||
"vscode-textmate": "1.0.11",
|
||||
"winreg": "1.2.0",
|
||||
"yauzl": "2.3.1"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
ARGS=$@
|
||||
|
||||
# If root, ensure that --user-data-dir is specified
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
while test $# -gt 0
|
||||
|
@ -35,5 +33,5 @@ fi
|
|||
|
||||
ELECTRON="$VSCODE_PATH/@@NAME@@"
|
||||
CLI="$VSCODE_PATH/resources/app/out/cli.js"
|
||||
ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 "$ELECTRON" "$CLI" $ARGS
|
||||
ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"
|
||||
exit $?
|
|
@ -12,6 +12,11 @@ ln -s /usr/share/@@NAME@@/bin/@@NAME@@ /usr/bin/@@NAME@@
|
|||
# developers would prefer a terminal editor as the default.
|
||||
update-alternatives --install /usr/bin/editor editor /usr/bin/@@NAME@@ 0
|
||||
|
||||
# Install the desktop entry
|
||||
if hash desktop-file-install 2>/dev/null; then
|
||||
desktop-file-install /usr/share/applications/@@NAME@@.desktop
|
||||
fi
|
||||
|
||||
if [ "@@NAME@@" != "code-oss" ]; then
|
||||
# Remove the legacy bin command if this is the stable build
|
||||
if [ "@@NAME@@" = "code" ]; then
|
||||
|
|
6
resources/linux/debian/postrm.template
Executable file
6
resources/linux/debian/postrm.template
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
rm -f /usr/bin/@@NAME@@
|
|
@ -8,7 +8,7 @@ Packager: Visual Studio Code Team <vscode-linux@microsoft.com>
|
|||
License: MIT
|
||||
URL: https://code.visualstudio.com/
|
||||
Icon: @@NAME@@.xpm
|
||||
Requires: git
|
||||
Requires: git, glibc >= 2.15
|
||||
AutoReq: 0
|
||||
|
||||
%description
|
||||
|
@ -28,9 +28,9 @@ if [ "@@NAME@@" = "code" ]; then
|
|||
rm -f /usr/local/bin/code
|
||||
fi
|
||||
|
||||
# Symlink bin command to /usr/bin2
|
||||
rm -f /usr/bin/@@NAME@@
|
||||
ln -s /usr/share/@@NAME@@/bin/@@NAME@@ /usr/bin/@@NAME@@
|
||||
# Symlink bin command to /usr/bin
|
||||
sudo rm -f /usr/bin/@@NAME@@
|
||||
sudo ln -s /usr/share/@@NAME@@/bin/@@NAME@@ /usr/bin/@@NAME@@
|
||||
|
||||
# Register yum repository
|
||||
# TODO: #229: Enable once the yum repository is signed
|
||||
|
@ -42,6 +42,9 @@ ln -s /usr/share/@@NAME@@/bin/@@NAME@@ /usr/bin/@@NAME@@
|
|||
# fi
|
||||
#fi
|
||||
|
||||
%postun
|
||||
sudo rm -f /usr/bin/@@NAME@@
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ function code() {
|
|||
test -d node_modules || ./scripts/npm.sh install
|
||||
|
||||
# Get electron
|
||||
./node_modules/.bin/gulp electron
|
||||
test -d .build/electron || ./node_modules/.bin/gulp electron
|
||||
|
||||
# Build
|
||||
test -d out || ./node_modules/.bin/gulp compile
|
||||
|
@ -31,7 +31,7 @@ function code() {
|
|||
VSCODE_DEV=1 \
|
||||
ELECTRON_ENABLE_LOGGING=1 \
|
||||
ELECTRON_ENABLE_STACK_DUMPING=1 \
|
||||
"$ELECTRON" "$CLI" . $@
|
||||
"$ELECTRON" "$CLI" . "$@"
|
||||
}
|
||||
|
||||
code "$@"
|
||||
|
|
|
@ -9,7 +9,7 @@ pushd %~dp0\..
|
|||
if not exist node_modules call .\scripts\npm.bat install
|
||||
|
||||
:: Get electron
|
||||
node .\node_modules\gulp\bin\gulp.js electron
|
||||
if not exist .build\electron node .\node_modules\gulp\bin\gulp.js electron
|
||||
|
||||
:: Build
|
||||
if not exist out node .\node_modules\gulp\bin\gulp.js compile
|
||||
|
|
|
@ -14,7 +14,7 @@ function code() {
|
|||
test -d node_modules || ./scripts/npm.sh install
|
||||
|
||||
# Get electron
|
||||
./node_modules/.bin/gulp electron
|
||||
test -d .build/electron || ./node_modules/.bin/gulp electron
|
||||
|
||||
# Build
|
||||
test -d out || ./node_modules/.bin/gulp compile
|
||||
|
|
66
src/main.js
66
src/main.js
|
@ -9,6 +9,8 @@ global.vscodeStart = Date.now();
|
|||
var app = require('electron').app;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var paths = require('./paths');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
function stripComments(content) {
|
||||
var regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
|
||||
|
@ -50,11 +52,11 @@ function getNLSConfiguration() {
|
|||
|
||||
if (!locale) {
|
||||
var userData = app.getPath('userData');
|
||||
localeConfig = path.join(userData, 'User', 'locale.json');
|
||||
var localeConfig = path.join(userData, 'User', 'locale.json');
|
||||
if (fs.existsSync(localeConfig)) {
|
||||
try {
|
||||
var content = stripComments(fs.readFileSync(localeConfig, 'utf8'));
|
||||
value = JSON.parse(content).locale;
|
||||
var value = JSON.parse(content).locale;
|
||||
if (value && typeof value === 'string') {
|
||||
locale = value;
|
||||
}
|
||||
|
@ -63,7 +65,8 @@ function getNLSConfiguration() {
|
|||
}
|
||||
}
|
||||
|
||||
locale = locale || app.getLocale();
|
||||
var appLocale = app.getLocale();
|
||||
locale = locale || appLocale;
|
||||
// Language tags are case insensitve however an amd loader is case sensitive
|
||||
// To make this work on case preserving & insensitive FS we do the following:
|
||||
// the language bundles have lower case language tags and we always lower case
|
||||
|
@ -76,23 +79,38 @@ function getNLSConfiguration() {
|
|||
if (process.env['VSCODE_DEV']) {
|
||||
return { locale: locale, availableLanguages: {} };
|
||||
}
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
while (locale) {
|
||||
var candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js';
|
||||
if (fs.existsSync(candidate)) {
|
||||
return { locale: initialLocale, availableLanguages: { '*': locale } };
|
||||
} else {
|
||||
var index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
locale = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have an English locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale == 'en' || locale.startsWith('en-'))) {
|
||||
return { locale: locale, availableLanguages: {} };
|
||||
}
|
||||
|
||||
return { locale: initialLocale, availableLanguages: {} };
|
||||
function resolveLocale(locale) {
|
||||
while (locale) {
|
||||
var candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js';
|
||||
if (fs.existsSync(candidate)) {
|
||||
return { locale: initialLocale, availableLanguages: { '*': locale } };
|
||||
} else {
|
||||
var index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
locale = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var resolvedLocale = resolveLocale(locale);
|
||||
if (!resolvedLocale && appLocale && appLocale !== locale) {
|
||||
resolvedLocale = resolveLocale(appLocale);
|
||||
}
|
||||
return resolvedLocale ? resolvedLocale : { locale: initialLocale, availableLanguages: {} };
|
||||
}
|
||||
|
||||
// Update cwd based on environment and platform
|
||||
|
@ -107,19 +125,9 @@ try {
|
|||
console.error(err);
|
||||
}
|
||||
|
||||
// Set path according to being built or not
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
var appData = app.getPath('appData');
|
||||
app.setPath('userData', path.join(appData, 'Code-Development'));
|
||||
}
|
||||
|
||||
// Use custom user data dir if specified, required to run as root on Linux
|
||||
var args = process.argv;
|
||||
args.forEach(function (arg) {
|
||||
if (arg.indexOf('--user-data-dir=') === 0) {
|
||||
app.setPath('userData', arg.split('=')[1]);
|
||||
}
|
||||
});
|
||||
// Set userData path before app 'ready' event
|
||||
var userData = paths.getUserDataPath(process.platform, pkg.name, process.argv);
|
||||
app.setPath('userData', userData);
|
||||
|
||||
// Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
|
||||
// the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
|
||||
|
|
32
src/paths.js
Normal file
32
src/paths.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
var minimist = require('minimist');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
|
||||
function getAppDataPath(platform) {
|
||||
switch (platform) {
|
||||
case 'win32': return process.env['APPDATA'];
|
||||
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
|
||||
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
default: throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
function getUserDataPath(platform, appName, args) {
|
||||
var argv = minimist(args, { string: ['user-data-dir'] });
|
||||
var userDataDir = argv['user-data-dir'];
|
||||
var appData = getAppDataPath(platform);
|
||||
|
||||
if (userDataDir) {
|
||||
return userDataDir;
|
||||
}
|
||||
|
||||
return path.join(appData, appName);
|
||||
}
|
||||
|
||||
exports.getAppDataPath = getAppDataPath;
|
||||
exports.getUserDataPath = getUserDataPath;
|
23
src/typings/pty.js.d.ts
vendored
Normal file
23
src/typings/pty.js.d.ts
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'pty.js' {
|
||||
export function fork(file: string, args: string[], options: any): Terminal;
|
||||
export function spawn(file: string, args: string[], options: any): Terminal;
|
||||
export function createTerminal(file: string, args: string[], options: any): Terminal;
|
||||
|
||||
export interface Terminal {
|
||||
/**
|
||||
* The title of the active process.
|
||||
*/
|
||||
process: string;
|
||||
|
||||
on(event: string, callback: (data: any) => void): void;
|
||||
|
||||
resize(columns: number, rows: number): void;
|
||||
|
||||
write(data: string): void;
|
||||
}
|
||||
}
|
17
src/typings/term.js.d.ts
vendored
Normal file
17
src/typings/term.js.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'term.js' {
|
||||
function init(options: any): TermJsTerminal;
|
||||
|
||||
// There seems to be no way to export this so it can be referenced outside of this file when a
|
||||
// module is a function.
|
||||
interface TermJsTerminal {
|
||||
on(event: string, callback: (data: any) => void): void;
|
||||
resize(columns: number, rows: number): void;
|
||||
}
|
||||
|
||||
export = init;
|
||||
}
|
|
@ -6,46 +6,42 @@
|
|||
|
||||
import types = require('vs/base/common/types');
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
|
||||
interface ISafeWindow {
|
||||
Worker: any;
|
||||
class ZoomManager {
|
||||
|
||||
public static INSTANCE = new ZoomManager();
|
||||
|
||||
private _zoomLevel: number = 0;
|
||||
|
||||
private _onDidChangeZoomLevel: Emitter<number> = new Emitter<number>();
|
||||
public onDidChangeZoomLevel:Event<number> = this._onDidChangeZoomLevel.event;
|
||||
public getZoomLevel(): number {
|
||||
return this._zoomLevel;
|
||||
}
|
||||
|
||||
public setZoomLevel(zoomLevel:number): void {
|
||||
if (this._zoomLevel === zoomLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._zoomLevel = zoomLevel;
|
||||
this._onDidChangeZoomLevel.fire(this._zoomLevel);
|
||||
}
|
||||
}
|
||||
|
||||
interface ISafeDocument {
|
||||
URL: string;
|
||||
createElement(tagName: 'div'): HTMLDivElement;
|
||||
createElement(tagName: string): HTMLElement;
|
||||
export function getZoomLevel(): number {
|
||||
return ZoomManager.INSTANCE.getZoomLevel();
|
||||
}
|
||||
export function setZoomLevel(zoomLevel:number): void {
|
||||
ZoomManager.INSTANCE.setZoomLevel(zoomLevel);
|
||||
}
|
||||
export function onDidChangeZoomLevel(callback:(zoomLevel:number)=>void): IDisposable {
|
||||
return ZoomManager.INSTANCE.onDidChangeZoomLevel(callback);
|
||||
}
|
||||
|
||||
interface INavigator {
|
||||
userAgent: string;
|
||||
}
|
||||
|
||||
interface IGlobalScope {
|
||||
navigator: INavigator;
|
||||
document: ISafeDocument;
|
||||
history: {
|
||||
pushState: any
|
||||
};
|
||||
}
|
||||
|
||||
const globals = <IGlobalScope><any>(typeof self === 'object' ? self : global);
|
||||
|
||||
// MAC:
|
||||
// chrome: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.100 Safari/535.2"
|
||||
// safari: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/534.51.22"
|
||||
//
|
||||
// WINDOWS:
|
||||
// chrome: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.102 Safari/535.2"
|
||||
// IE: "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; MS-RTC LM 8; InfoPath.3; Zune 4.7)"
|
||||
// Opera: "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.9.168 Version/11.52"
|
||||
// FF: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0) Gecko/20100101 Firefox/8.0"
|
||||
|
||||
// LINUX:
|
||||
// chrome: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36"
|
||||
// firefox: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:34.0) Gecko/20100101 Firefox/34.0"
|
||||
|
||||
const userAgent = globals.navigator ? globals.navigator.userAgent : '';
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
// DOCUMENTED FOR FUTURE REFERENCE:
|
||||
// When running IE11 in IE10 document mode, the code below will identify the browser as being IE10,
|
||||
|
@ -68,22 +64,6 @@ export const canUseTranslate3d = !isIE9 && !isFirefox;
|
|||
|
||||
export const enableEmptySelectionClipboard = isWebKit;
|
||||
|
||||
let _disablePushState = false;
|
||||
|
||||
/**
|
||||
* Returns if the browser supports the history.pushState function or not.
|
||||
*/
|
||||
export function canPushState() {
|
||||
return (!_disablePushState && globals.history && globals.history.pushState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpful when we detect that pushing state does not work for some reason (e.g. FF prevents pushState for security reasons in some cases)
|
||||
*/
|
||||
export function disablePushState() {
|
||||
_disablePushState = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the browser supports CSS 3 animations.
|
||||
*/
|
||||
|
@ -92,12 +72,8 @@ export function hasCSSAnimationSupport() {
|
|||
return this._hasCSSAnimationSupport;
|
||||
}
|
||||
|
||||
if (!globals.document) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let supported = false;
|
||||
let element = globals.document.createElement('div');
|
||||
let element = document.createElement('div');
|
||||
let properties = ['animationName', 'webkitAnimationName', 'msAnimationName', 'MozAnimationName', 'OAnimationName'];
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let property = properties[i];
|
||||
|
@ -116,10 +92,6 @@ export function hasCSSAnimationSupport() {
|
|||
return this._hasCSSAnimationSupport;
|
||||
}
|
||||
|
||||
export function isInWebWorker(): boolean {
|
||||
return !globals.document && typeof ((<any>globals).importScripts) !== 'undefined';
|
||||
}
|
||||
|
||||
export function supportsExecCommand(command: string): boolean {
|
||||
return (
|
||||
(isIE11orEarlier || Platform.isNative)
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {isInWebWorker} from 'vs/base/browser/browser';
|
||||
|
||||
export interface IBrowserServiceData {
|
||||
document: Document;
|
||||
window: Window;
|
||||
isHTMLElement: (o: any) => boolean;
|
||||
}
|
||||
|
||||
export interface IBrowserService extends IBrowserServiceData {
|
||||
/**
|
||||
* Mock the DOM with dummy objects
|
||||
*/
|
||||
mock(source: IBrowserServiceData): void;
|
||||
|
||||
/**
|
||||
* Restore the normal DOM
|
||||
*/
|
||||
restore(): void;
|
||||
}
|
||||
|
||||
export function regularIsHTMLElement(o: any): boolean {
|
||||
if (typeof HTMLElement === 'object') {
|
||||
return o instanceof HTMLElement;
|
||||
}
|
||||
return o && typeof o === 'object' && o.nodeType === 1 && typeof o.nodeName === 'string';
|
||||
}
|
||||
|
||||
class BrowserService implements IBrowserService {
|
||||
|
||||
public document: Document;
|
||||
public window: Window;
|
||||
public isHTMLElement: (o: any) => boolean;
|
||||
|
||||
constructor() {
|
||||
this.restore();
|
||||
}
|
||||
|
||||
public mock(source: IBrowserServiceData): void {
|
||||
this.document = source.document;
|
||||
this.window = source.window;
|
||||
this.isHTMLElement = source.isHTMLElement;
|
||||
}
|
||||
|
||||
public restore(): void {
|
||||
this.isHTMLElement = regularIsHTMLElement;
|
||||
if (isInWebWorker()) {
|
||||
this.document = null;
|
||||
this.window = null;
|
||||
} else {
|
||||
this.document = window.document;
|
||||
this.window = window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const browserService = new BrowserService();
|
||||
|
||||
export function getService(): IBrowserService {
|
||||
return browserService;
|
||||
}
|
|
@ -11,7 +11,6 @@ import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
|||
import strings = require('vs/base/common/strings');
|
||||
import assert = require('vs/base/common/assert');
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import BrowserService = require('vs/base/browser/browserService');
|
||||
|
||||
/**
|
||||
* Welcome to the monaco builder. The recommended way to use it is:
|
||||
|
@ -52,7 +51,7 @@ export interface QuickBuilder {
|
|||
export function withElementById(id: string, offdom?: boolean): Builder {
|
||||
assert.ok(types.isString(id), 'Expected String as parameter');
|
||||
|
||||
let element = BrowserService.getService().document.getElementById(id);
|
||||
let element = document.getElementById(id);
|
||||
if (element) {
|
||||
return new Builder(element, offdom);
|
||||
}
|
||||
|
@ -136,7 +135,6 @@ export class Builder implements IDisposable {
|
|||
private createdElements: HTMLElement[];
|
||||
private toUnbind: { [type: string]: IDisposable[]; };
|
||||
private captureToUnbind: { [type: string]: IDisposable[]; };
|
||||
private browserService: BrowserService.IBrowserService;
|
||||
|
||||
constructor(element?: HTMLElement, offdom?: boolean) {
|
||||
this.offdom = offdom;
|
||||
|
@ -148,7 +146,6 @@ export class Builder implements IDisposable {
|
|||
|
||||
this.toUnbind = {};
|
||||
this.captureToUnbind = {};
|
||||
this.browserService = BrowserService.getService();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -476,7 +473,7 @@ export class Builder implements IDisposable {
|
|||
private doElement(name: string, attributesOrFn?: any, fn?: (builder: Builder) => void): Builder {
|
||||
|
||||
// Create Element
|
||||
let element = this.browserService.document.createElement(name);
|
||||
let element = document.createElement(name);
|
||||
this.currentElement = element;
|
||||
|
||||
// Off-DOM: Remember in array of created elements
|
||||
|
@ -521,7 +518,7 @@ export class Builder implements IDisposable {
|
|||
* Returns true if the current element of this builder is the active element.
|
||||
*/
|
||||
public hasFocus(): boolean {
|
||||
let activeElement: Element = this.browserService.document.activeElement;
|
||||
let activeElement: Element = document.activeElement;
|
||||
|
||||
return (activeElement === this.currentElement);
|
||||
}
|
||||
|
@ -1535,7 +1532,7 @@ export class Builder implements IDisposable {
|
|||
else {
|
||||
// if there are elements inside this node, append the string as a new text node
|
||||
// to avoid wiping out the innerHTML and replacing it with only text content
|
||||
this.currentElement.appendChild(this.browserService.document.createTextNode(text));
|
||||
this.currentElement.appendChild(document.createTextNode(text));
|
||||
}
|
||||
} else {
|
||||
this.currentElement.textContent = text;
|
||||
|
@ -1848,24 +1845,24 @@ export class Builder implements IDisposable {
|
|||
public getClientArea(): Dimension {
|
||||
|
||||
// 0.) Try with DOM getDomNodePosition
|
||||
if (this.currentElement !== this.browserService.document.body) {
|
||||
if (this.currentElement !== document.body) {
|
||||
let dimensions = DOM.getDomNodePosition(this.currentElement);
|
||||
return new Dimension(dimensions.width, dimensions.height);
|
||||
}
|
||||
|
||||
// 1.) Try innerWidth / innerHeight
|
||||
if (this.browserService.window.innerWidth && this.browserService.window.innerHeight) {
|
||||
return new Dimension(this.browserService.window.innerWidth, this.browserService.window.innerHeight);
|
||||
if (window.innerWidth && window.innerHeight) {
|
||||
return new Dimension(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
// 2.) Try with document.body.clientWidth / document.body.clientHeigh
|
||||
if (this.browserService.document.body && this.browserService.document.body.clientWidth && this.browserService.document.body.clientWidth) {
|
||||
return new Dimension(this.browserService.document.body.clientWidth, this.browserService.document.body.clientHeight);
|
||||
if (document.body && document.body.clientWidth && document.body.clientWidth) {
|
||||
return new Dimension(document.body.clientWidth, document.body.clientHeight);
|
||||
}
|
||||
|
||||
// 3.) Try with document.documentElement.clientWidth / document.documentElement.clientHeight
|
||||
if (this.browserService.document.documentElement && this.browserService.document.documentElement.clientWidth && this.browserService.document.documentElement.clientHeight) {
|
||||
return new Dimension(this.browserService.document.documentElement.clientWidth, this.browserService.document.documentElement.clientHeight);
|
||||
if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientHeight) {
|
||||
return new Dimension(document.documentElement.clientWidth, document.documentElement.clientHeight);
|
||||
}
|
||||
|
||||
throw new Error('Unable to figure out browser width and height');
|
||||
|
@ -2154,7 +2151,7 @@ export let $: QuickBuilder = function(arg?: any): Builder {
|
|||
// Use the argument as HTML code
|
||||
if (arg[0] === '<') {
|
||||
let element: Node;
|
||||
let container = BrowserService.getService().document.createElement('div');
|
||||
let container = document.createElement('div');
|
||||
container.innerHTML = strings.format.apply(strings, arguments);
|
||||
|
||||
if (container.children.length === 0) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import {EventEmitter} from 'vs/base/common/eventEmitter';
|
|||
import {Disposable, IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {isObject} from 'vs/base/common/types';
|
||||
import {isChrome, isWebKit} from 'vs/base/browser/browser';
|
||||
import {getService} from 'vs/base/browser/browserService';
|
||||
import {IKeyboardEvent, StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent';
|
||||
import {IMouseEvent, StandardMouseEvent} from 'vs/base/browser/mouseEvent';
|
||||
|
||||
|
@ -764,7 +763,10 @@ export function removeCSSRulesWithPrefix(ruleName: string, style = sharedStyle):
|
|||
}
|
||||
|
||||
export function isHTMLElement(o: any): o is HTMLElement {
|
||||
return getService().isHTMLElement(o);
|
||||
if (typeof HTMLElement === 'object') {
|
||||
return o instanceof HTMLElement;
|
||||
}
|
||||
return o && typeof o === 'object' && o.nodeType === 1 && typeof o.nodeName === 'string';
|
||||
}
|
||||
|
||||
export const EventType = {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import {TimeoutTimer} from 'vs/base/common/async';
|
||||
import {EventEmitter} from 'vs/base/common/eventEmitter';
|
||||
import {Disposable, IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {getService} from 'vs/base/browser/browserService';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
export enum UserStatus {
|
||||
|
@ -34,8 +33,8 @@ export class IdleMonitor extends Disposable {
|
|||
this._idleTime = idleTime;
|
||||
|
||||
this._eventEmitter = this._register(new EventEmitter());
|
||||
this._register(dom.addDisposableListener(getService().document, 'mousemove', () => this._onUserActive()));
|
||||
this._register(dom.addDisposableListener(getService().document, 'keydown', () => this._onUserActive()));
|
||||
this._register(dom.addDisposableListener(document, 'mousemove', () => this._onUserActive()));
|
||||
this._register(dom.addDisposableListener(document, 'keydown', () => this._onUserActive()));
|
||||
this._onUserActive();
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,12 @@ export class FileLabel {
|
|||
let parent = paths.dirname(this.path);
|
||||
if (parent && parent !== '.') {
|
||||
let pathLabel = getPathLabel(parent, this.basepath);
|
||||
htmlContent.push('<span class="file-path" title="' + pathLabel + '">');
|
||||
htmlContent.push('<span class="file-path">');
|
||||
htmlContent.push(pathLabel);
|
||||
htmlContent.push('</span>');
|
||||
}
|
||||
|
||||
this.domNode.title = this.path;
|
||||
this.domNode.innerHTML = htmlContent.join('');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import 'vs/css!./inputBox';
|
|||
import nls = require('vs/nls');
|
||||
import * as Bal from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as browser from 'vs/base/browser/browserService';
|
||||
import {IHTMLContentElement} from 'vs/base/common/htmlContent';
|
||||
import {renderHtml} from 'vs/base/browser/htmlContentRenderer';
|
||||
import aria = require('vs/base/browser/ui/aria/aria');
|
||||
|
@ -204,7 +203,7 @@ export class InputBox extends Widget {
|
|||
}
|
||||
|
||||
public hasFocus(): boolean {
|
||||
return browser.getService().document.activeElement === this.input;
|
||||
return document.activeElement === this.input;
|
||||
}
|
||||
|
||||
public select(range: IRange = null): void {
|
||||
|
|
|
@ -8,6 +8,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
|||
import { Gesture } from 'vs/base/browser/touch';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { RangeMap, IRange } from './rangeMap';
|
||||
import { IDelegate, IRenderer } from './list';
|
||||
import { RowCache, IRow } from './rowCache';
|
||||
|
@ -87,9 +88,9 @@ export class ListView<T> implements IDisposable {
|
|||
this.gesture = new Gesture(this.rowsContainer);
|
||||
|
||||
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
|
||||
forbidTranslate3dUse: true,
|
||||
horizontal: 'hidden',
|
||||
vertical: 'auto',
|
||||
canUseTranslate3d: false,
|
||||
horizontal: ScrollbarVisibility.Hidden,
|
||||
vertical: ScrollbarVisibility.Auto,
|
||||
useShadows: false,
|
||||
saveLastScrollTimeOnClassName: 'monaco-list-row'
|
||||
});
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as Browser from 'vs/base/browser/browser';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
import {IMouseEvent, StandardMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
|
||||
|
@ -13,8 +12,9 @@ import {Widget} from 'vs/base/browser/ui/widget';
|
|||
import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
|
||||
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import {ScrollbarArrow, ScrollbarArrowOptions} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
import {Visibility, ScrollbarVisibilityController} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
import {ScrollbarVisibilityController} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
import {Scrollable} from 'vs/base/common/scrollable';
|
||||
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
|
||||
/**
|
||||
* The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
|
||||
|
@ -34,18 +34,18 @@ export interface ScrollbarHost {
|
|||
}
|
||||
|
||||
export interface AbstractScrollbarOptions {
|
||||
forbidTranslate3dUse: boolean;
|
||||
canUseTranslate3d: boolean;
|
||||
lazyRender:boolean;
|
||||
host: ScrollbarHost;
|
||||
scrollbarState: ScrollbarState;
|
||||
visibility: Visibility;
|
||||
visibility: ScrollbarVisibility;
|
||||
extraScrollbarClassName: string;
|
||||
scrollable: Scrollable;
|
||||
}
|
||||
|
||||
export abstract class AbstractScrollbar extends Widget {
|
||||
|
||||
protected _forbidTranslate3dUse: boolean;
|
||||
protected _canUseTranslate3d: boolean;
|
||||
protected _host: ScrollbarHost;
|
||||
protected _scrollable: Scrollable;
|
||||
private _lazyRender: boolean;
|
||||
|
@ -60,7 +60,7 @@ export abstract class AbstractScrollbar extends Widget {
|
|||
|
||||
constructor(opts:AbstractScrollbarOptions) {
|
||||
super();
|
||||
this._forbidTranslate3dUse = opts.forbidTranslate3dUse;
|
||||
this._canUseTranslate3d = opts.canUseTranslate3d;
|
||||
this._lazyRender = opts.lazyRender;
|
||||
this._host = opts.host;
|
||||
this._scrollable = opts.scrollable;
|
||||
|
@ -69,10 +69,6 @@ export abstract class AbstractScrollbar extends Widget {
|
|||
this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
|
||||
this._shouldRender = true;
|
||||
this.domNode = createFastDomNode(document.createElement('div'));
|
||||
if (!this._forbidTranslate3dUse && Browser.canUseTranslate3d) {
|
||||
// Put the scrollbar in its own layer
|
||||
this.domNode.setTransform('translate3d(0px, 0px, 0px)');
|
||||
}
|
||||
|
||||
this._visibilityController.setDomNode(this.domNode);
|
||||
this.domNode.setPosition('absolute');
|
||||
|
@ -110,6 +106,11 @@ export abstract class AbstractScrollbar extends Widget {
|
|||
|
||||
// ----------------- Update state
|
||||
|
||||
public setCanUseTranslate3d(canUseTranslate3d: boolean): boolean {
|
||||
this._canUseTranslate3d = canUseTranslate3d;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected _onElementSize(visibleSize: number): boolean {
|
||||
if (this._scrollbarState.setVisibleSize(visibleSize)) {
|
||||
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
|
||||
|
@ -159,6 +160,13 @@ export abstract class AbstractScrollbar extends Widget {
|
|||
}
|
||||
this._shouldRender = false;
|
||||
|
||||
if (this._canUseTranslate3d) {
|
||||
// Put the scrollbar in its own layer
|
||||
this.domNode.setTransform('translate3d(0px, 0px, 0px)');
|
||||
} else {
|
||||
this.domNode.setTransform('');
|
||||
}
|
||||
|
||||
this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
|
||||
this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
|
||||
}
|
||||
|
|
|
@ -4,27 +4,25 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as Browser from 'vs/base/browser/browser';
|
||||
import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
|
||||
import {IDomNodePosition} from 'vs/base/browser/dom';
|
||||
import {ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {ScrollbarVisibility, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {Scrollable, ScrollEvent} from 'vs/base/common/scrollable';
|
||||
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import {ARROW_IMG_SIZE} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
|
||||
export class HorizontalScrollbar extends AbstractScrollbar {
|
||||
|
||||
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
|
||||
super({
|
||||
forbidTranslate3dUse: options.forbidTranslate3dUse,
|
||||
canUseTranslate3d: options.canUseTranslate3d,
|
||||
lazyRender: options.lazyRender,
|
||||
host: host,
|
||||
scrollbarState: new ScrollbarState(
|
||||
(options.horizontalHasArrows ? options.arrowSize : 0),
|
||||
(options.horizontal === Visibility.Hidden ? 0 : options.horizontalScrollbarSize),
|
||||
(options.vertical === Visibility.Hidden ? 0 : options.verticalScrollbarSize)
|
||||
(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize),
|
||||
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize)
|
||||
),
|
||||
visibility: options.horizontal,
|
||||
extraScrollbarClassName: 'horizontal',
|
||||
|
@ -63,9 +61,11 @@ export class HorizontalScrollbar extends AbstractScrollbar {
|
|||
|
||||
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
|
||||
this.slider.setWidth(sliderSize);
|
||||
if (!this._forbidTranslate3dUse && Browser.canUseTranslate3d) {
|
||||
if (this._canUseTranslate3d) {
|
||||
this.slider.setTransform('translate3d(' + sliderPosition + 'px, 0px, 0px)');
|
||||
this.slider.setLeft(0);
|
||||
} else {
|
||||
this.slider.setTransform('');
|
||||
this.slider.setLeft(sliderPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import 'vs/css!./media/scrollbars';
|
||||
|
||||
import * as Browser from 'vs/base/browser/browser';
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import {StandardMouseWheelEvent, IMouseEvent} from 'vs/base/browser/mouseEvent';
|
||||
import {HorizontalScrollbar} from 'vs/base/browser/ui/scrollbar/horizontalScrollbar';
|
||||
import {VerticalScrollbar} from 'vs/base/browser/ui/scrollbar/verticalScrollbar';
|
||||
import {ScrollableElementCreationOptions, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {visibilityFromString} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
import {ScrollbarVisibility, ScrollableElementCreationOptions, ScrollableElementChangeOptions, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {Scrollable, ScrollEvent, INewScrollState} from 'vs/base/common/scrollable';
|
||||
import {Widget} from 'vs/base/browser/ui/widget';
|
||||
|
@ -181,12 +181,18 @@ export class ScrollableElement extends Widget {
|
|||
* Really this is Editor.IEditorScrollbarOptions, but base shouldn't
|
||||
* depend on Editor.
|
||||
*/
|
||||
public updateOptions(newOptions: ScrollableElementCreationOptions): void {
|
||||
// only support handleMouseWheel changes for now
|
||||
public updateOptions(newOptions: ScrollableElementChangeOptions): void {
|
||||
let massagedOptions = resolveOptions(newOptions);
|
||||
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
|
||||
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
|
||||
this._setListeningToMouseWheel(this._options.handleMouseWheel);
|
||||
|
||||
this._shouldRender = this._horizontalScrollbar.setCanUseTranslate3d(massagedOptions.canUseTranslate3d) || this._shouldRender;
|
||||
this._shouldRender = this._verticalScrollbar.setCanUseTranslate3d(massagedOptions.canUseTranslate3d) || this._shouldRender;
|
||||
|
||||
if (!this._options.lazyRender) {
|
||||
this._render();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- mouse wheel scrolling --------------------
|
||||
|
@ -400,7 +406,7 @@ export class DomScrollableElement extends ScrollableElement {
|
|||
|
||||
function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableElementResolvedOptions {
|
||||
let result: ScrollableElementResolvedOptions = {
|
||||
forbidTranslate3dUse: (typeof opts.forbidTranslate3dUse !== 'undefined' ? opts.forbidTranslate3dUse : false),
|
||||
canUseTranslate3d: opts.canUseTranslate3d && Browser.canUseTranslate3d,
|
||||
lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false),
|
||||
className: (typeof opts.className !== 'undefined' ? opts.className : ''),
|
||||
useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true),
|
||||
|
@ -411,12 +417,12 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
|
|||
|
||||
listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null),
|
||||
|
||||
horizontal: visibilityFromString(typeof opts.horizontal !== 'undefined' ? opts.horizontal : 'auto'),
|
||||
horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : ScrollbarVisibility.Auto),
|
||||
horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10),
|
||||
horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0),
|
||||
horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false),
|
||||
|
||||
vertical: visibilityFromString(typeof opts.vertical !== 'undefined' ? opts.vertical : 'auto'),
|
||||
vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto),
|
||||
verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10),
|
||||
verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false),
|
||||
verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0),
|
||||
|
|
|
@ -4,13 +4,17 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
export enum ScrollbarVisibility {
|
||||
Auto = 1,
|
||||
Hidden = 2,
|
||||
Visible = 3
|
||||
}
|
||||
|
||||
export interface ScrollableElementCreationOptions {
|
||||
/**
|
||||
* Prevent the scrollbar rendering from using translate3d. Defaults to false.
|
||||
* Allow scrollbar rendering to use translate3d.
|
||||
*/
|
||||
forbidTranslate3dUse?: boolean;
|
||||
canUseTranslate3d: boolean;
|
||||
/**
|
||||
* The scrollable element should not do any DOM mutations until renderNow() is called.
|
||||
* Defaults to false.
|
||||
|
@ -55,7 +59,7 @@ export interface ScrollableElementCreationOptions {
|
|||
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
|
||||
* Defaults to 'auto'.
|
||||
*/
|
||||
horizontal?: string;
|
||||
horizontal?: ScrollbarVisibility;
|
||||
/**
|
||||
* Height (in px) of the horizontal scrollbar.
|
||||
* Defaults to 10.
|
||||
|
@ -76,7 +80,7 @@ export interface ScrollableElementCreationOptions {
|
|||
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
|
||||
* Defaults to 'auto'.
|
||||
*/
|
||||
vertical?: string;
|
||||
vertical?: ScrollbarVisibility;
|
||||
/**
|
||||
* Width (in px) of the vertical scrollbar.
|
||||
* Defaults to 10.
|
||||
|
@ -98,8 +102,14 @@ export interface ScrollableElementCreationOptions {
|
|||
saveLastScrollTimeOnClassName?: string;
|
||||
}
|
||||
|
||||
export interface ScrollableElementChangeOptions {
|
||||
canUseTranslate3d: boolean;
|
||||
handleMouseWheel?: boolean;
|
||||
mouseWheelScrollSensitivity?: number;
|
||||
}
|
||||
|
||||
export interface ScrollableElementResolvedOptions {
|
||||
forbidTranslate3dUse: boolean;
|
||||
canUseTranslate3d: boolean;
|
||||
lazyRender: boolean;
|
||||
className: string;
|
||||
useShadows: boolean;
|
||||
|
@ -108,11 +118,11 @@ export interface ScrollableElementResolvedOptions {
|
|||
mouseWheelScrollSensitivity: number;
|
||||
arrowSize: number;
|
||||
listenOnDomNode: HTMLElement;
|
||||
horizontal: Visibility;
|
||||
horizontal: ScrollbarVisibility;
|
||||
horizontalScrollbarSize: number;
|
||||
horizontalSliderSize: number;
|
||||
horizontalHasArrows: boolean;
|
||||
vertical: Visibility;
|
||||
vertical: ScrollbarVisibility;
|
||||
verticalScrollbarSize: number;
|
||||
verticalSliderSize: number;
|
||||
verticalHasArrows: boolean;
|
||||
|
|
|
@ -7,26 +7,10 @@
|
|||
import {Disposable} from 'vs/base/common/lifecycle';
|
||||
import {TimeoutTimer} from 'vs/base/common/async';
|
||||
import {FastDomNode} from 'vs/base/browser/styleMutator';
|
||||
|
||||
export enum Visibility {
|
||||
Auto,
|
||||
Hidden,
|
||||
Visible
|
||||
}
|
||||
|
||||
export function visibilityFromString(visibility: string): Visibility {
|
||||
switch (visibility) {
|
||||
case 'hidden':
|
||||
return Visibility.Hidden;
|
||||
case 'visible':
|
||||
return Visibility.Visible;
|
||||
default:
|
||||
return Visibility.Auto;
|
||||
}
|
||||
}
|
||||
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
|
||||
export class ScrollbarVisibilityController extends Disposable {
|
||||
private _visibility: Visibility;
|
||||
private _visibility: ScrollbarVisibility;
|
||||
private _visibleClassName: string;
|
||||
private _invisibleClassName: string;
|
||||
private _domNode: FastDomNode;
|
||||
|
@ -35,7 +19,7 @@ export class ScrollbarVisibilityController extends Disposable {
|
|||
private _isVisible: boolean;
|
||||
private _revealTimer: TimeoutTimer;
|
||||
|
||||
constructor(visibility: Visibility, visibleClassName: string, invisibleClassName: string) {
|
||||
constructor(visibility: ScrollbarVisibility, visibleClassName: string, invisibleClassName: string) {
|
||||
super();
|
||||
this._visibility = visibility;
|
||||
this._visibleClassName = visibleClassName;
|
||||
|
@ -50,10 +34,10 @@ export class ScrollbarVisibilityController extends Disposable {
|
|||
// ----------------- Hide / Reveal
|
||||
|
||||
private applyVisibilitySetting(shouldBeVisible: boolean): boolean {
|
||||
if (this._visibility === Visibility.Hidden) {
|
||||
if (this._visibility === ScrollbarVisibility.Hidden) {
|
||||
return false;
|
||||
}
|
||||
if (this._visibility === Visibility.Visible) {
|
||||
if (this._visibility === ScrollbarVisibility.Visible) {
|
||||
return true;
|
||||
}
|
||||
return shouldBeVisible;
|
||||
|
|
|
@ -4,26 +4,24 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as Browser from 'vs/base/browser/browser';
|
||||
import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
|
||||
import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
|
||||
import {IDomNodePosition} from 'vs/base/browser/dom';
|
||||
import {ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {ScrollbarVisibility, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import {Scrollable, ScrollEvent} from 'vs/base/common/scrollable';
|
||||
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
|
||||
import {ARROW_IMG_SIZE} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
|
||||
import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
|
||||
|
||||
export class VerticalScrollbar extends AbstractScrollbar {
|
||||
|
||||
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
|
||||
super({
|
||||
forbidTranslate3dUse: options.forbidTranslate3dUse,
|
||||
canUseTranslate3d: options.canUseTranslate3d,
|
||||
lazyRender: options.lazyRender,
|
||||
host: host,
|
||||
scrollbarState: new ScrollbarState(
|
||||
(options.verticalHasArrows ? options.arrowSize : 0),
|
||||
(options.vertical === Visibility.Hidden ? 0 : options.verticalScrollbarSize),
|
||||
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
|
||||
// give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom
|
||||
0
|
||||
),
|
||||
|
@ -64,9 +62,11 @@ export class VerticalScrollbar extends AbstractScrollbar {
|
|||
|
||||
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
|
||||
this.slider.setHeight(sliderSize);
|
||||
if (!this._forbidTranslate3dUse && Browser.canUseTranslate3d) {
|
||||
if (this._canUseTranslate3d) {
|
||||
this.slider.setTransform('translate3d(0px, ' + sliderPosition + 'px, 0px)');
|
||||
this.slider.setTop(0);
|
||||
} else {
|
||||
this.slider.setTransform('');
|
||||
this.slider.setTop(sliderPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,14 @@ function isThenable<T>(obj: any): obj is Thenable<T> {
|
|||
return obj && typeof (<Thenable<any>>obj).then === 'function';
|
||||
}
|
||||
|
||||
export function toThenable<T>(arg: T | Thenable<T>): Thenable<T> {
|
||||
if (isThenable(arg)) {
|
||||
return arg;
|
||||
} else {
|
||||
return TPromise.as(arg);
|
||||
}
|
||||
}
|
||||
|
||||
export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | Thenable<T>): TPromise<T> {
|
||||
let source = new CancellationTokenSource();
|
||||
return new TPromise<T>((resolve, reject) => {
|
||||
|
@ -28,6 +36,14 @@ export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | Th
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook a cancellation token to a WinJS Promise
|
||||
*/
|
||||
export function wireCancellationToken<T>(token: CancellationToken, promise: TPromise<T>): Thenable<T> {
|
||||
token.onCancellationRequested(() => promise.cancel());
|
||||
return promise;
|
||||
}
|
||||
|
||||
export interface ITask<T> {
|
||||
(): T;
|
||||
}
|
||||
|
@ -538,17 +554,10 @@ export class RunOnceScheduler {
|
|||
this.runner = runner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timeout. This change will only impact new schedule calls.
|
||||
*/
|
||||
public setTimeout(timeout: number): void {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel previous runner (if any) & schedule a new runner.
|
||||
*/
|
||||
public schedule(): void {
|
||||
public schedule(delay = this.timeout): void {
|
||||
this.cancel();
|
||||
this.timeoutToken = platform.setTimeout(this.timeoutHandler, this.timeout);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ export class IdGenerator {
|
|||
this._lastId = 0;
|
||||
}
|
||||
|
||||
public generate(): string {
|
||||
public nextId(): string {
|
||||
return this._prefix + (++this._lastId);
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultGenerator = new IdGenerator('id#');
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import {localize} from 'vs/nls';
|
||||
|
||||
export enum ScanError {
|
||||
None,
|
||||
|
@ -35,19 +35,50 @@ export enum SyntaxKind {
|
|||
EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* The scanner object, representing a JSON scanner at a position in the input string.
|
||||
*/
|
||||
export interface JSONScanner {
|
||||
/**
|
||||
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
|
||||
*/
|
||||
setPosition(pos: number);
|
||||
/**
|
||||
* Read the next token. Returns the tolen code.
|
||||
*/
|
||||
scan(): SyntaxKind;
|
||||
/**
|
||||
* Returns the current scan position, which is after the last read token.
|
||||
*/
|
||||
getPosition(): number;
|
||||
/**
|
||||
* Returns the last read token.
|
||||
*/
|
||||
getToken(): SyntaxKind;
|
||||
/**
|
||||
* Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false.
|
||||
*/
|
||||
getTokenValue(): string;
|
||||
/**
|
||||
* The start offset of the last read token.
|
||||
*/
|
||||
getTokenOffset(): number;
|
||||
/**
|
||||
* The length of the last read token.
|
||||
*/
|
||||
getTokenLength(): number;
|
||||
/**
|
||||
* An error code of the last scan.
|
||||
*/
|
||||
getTokenError(): ScanError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON scanner on the given text.
|
||||
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
||||
*/
|
||||
export function createScanner(text:string, ignoreTrivia:boolean = false):JSONScanner {
|
||||
|
||||
var pos = 0,
|
||||
let pos = 0,
|
||||
len = text.length,
|
||||
value:string = '',
|
||||
tokenOffset = 0,
|
||||
|
@ -55,10 +86,10 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
scanError:ScanError = ScanError.None;
|
||||
|
||||
function scanHexDigits(count: number, exact?: boolean): number {
|
||||
var digits = 0;
|
||||
var value = 0;
|
||||
let digits = 0;
|
||||
let value = 0;
|
||||
while (digits < count || !exact) {
|
||||
var ch = text.charCodeAt(pos);
|
||||
let ch = text.charCodeAt(pos);
|
||||
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
|
||||
value = value * 16 + ch - CharacterCodes._0;
|
||||
}
|
||||
|
@ -80,8 +111,16 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
return value;
|
||||
}
|
||||
|
||||
function setPosition(newPosition: number) {
|
||||
pos = newPosition;
|
||||
value = '';
|
||||
tokenOffset = 0;
|
||||
token = SyntaxKind.Unknown;
|
||||
scanError = ScanError.None;
|
||||
}
|
||||
|
||||
function scanNumber(): string {
|
||||
var start = pos;
|
||||
let start = pos;
|
||||
if (text.charCodeAt(pos) === CharacterCodes._0) {
|
||||
pos++;
|
||||
} else {
|
||||
|
@ -99,10 +138,10 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
}
|
||||
} else {
|
||||
scanError = ScanError.UnexpectedEndOfNumber;
|
||||
return text.substring(start, end);
|
||||
return text.substring(start, pos);
|
||||
}
|
||||
}
|
||||
var end = pos;
|
||||
let end = pos;
|
||||
if (pos < text.length && (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e)) {
|
||||
pos++;
|
||||
if (pos < text.length && text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) {
|
||||
|
@ -123,7 +162,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
|
||||
function scanString(): string {
|
||||
|
||||
var result = '',
|
||||
let result = '',
|
||||
start = pos;
|
||||
|
||||
while (true) {
|
||||
|
@ -132,7 +171,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
scanError = ScanError.UnexpectedEndOfString;
|
||||
break;
|
||||
}
|
||||
var ch = text.charCodeAt(pos);
|
||||
let ch = text.charCodeAt(pos);
|
||||
if (ch === CharacterCodes.doubleQuote) {
|
||||
result += text.substring(start, pos);
|
||||
pos++;
|
||||
|
@ -172,7 +211,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
result += '\t';
|
||||
break;
|
||||
case CharacterCodes.u:
|
||||
var ch = scanHexDigits(4, true);
|
||||
let ch = scanHexDigits(4, true);
|
||||
if (ch >= 0) {
|
||||
result += String.fromCharCode(ch);
|
||||
} else {
|
||||
|
@ -208,7 +247,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
return token = SyntaxKind.EOF;
|
||||
}
|
||||
|
||||
var code = text.charCodeAt(pos);
|
||||
let code = text.charCodeAt(pos);
|
||||
// trivia: whitespace
|
||||
if (isWhiteSpace(code)) {
|
||||
do {
|
||||
|
@ -260,7 +299,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
|
||||
// comments
|
||||
case CharacterCodes.slash:
|
||||
var start = pos - 1;
|
||||
let start = pos - 1;
|
||||
// Single-line comment
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
|
@ -280,10 +319,10 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) {
|
||||
pos += 2;
|
||||
|
||||
var safeLength = len - 1; // For lookahead.
|
||||
var commentClosed = false;
|
||||
let safeLength = len - 1; // For lookahead.
|
||||
let commentClosed = false;
|
||||
while (pos < safeLength) {
|
||||
var ch = text.charCodeAt(pos);
|
||||
let ch = text.charCodeAt(pos);
|
||||
|
||||
if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) {
|
||||
pos += 2;
|
||||
|
@ -371,7 +410,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
|
||||
|
||||
function scanNextNonTrivia():SyntaxKind {
|
||||
var result : SyntaxKind;
|
||||
let result : SyntaxKind;
|
||||
do {
|
||||
result = scanNext();
|
||||
} while (result >= SyntaxKind.LineCommentTrivia && result <= SyntaxKind.Trivia);
|
||||
|
@ -379,6 +418,7 @@ export function createScanner(text:string, ignoreTrivia:boolean = false):JSONSca
|
|||
}
|
||||
|
||||
return {
|
||||
setPosition: setPosition,
|
||||
getPosition: () => pos,
|
||||
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
|
||||
getToken: () => token,
|
||||
|
@ -403,10 +443,6 @@ function isDigit(ch: number): boolean {
|
|||
return ch >= CharacterCodes._0 && ch <= CharacterCodes._9;
|
||||
}
|
||||
|
||||
export function isLetter(ch: number): boolean {
|
||||
return ch >= CharacterCodes.a && ch <= CharacterCodes.z || ch >= CharacterCodes.A && ch <= CharacterCodes.Z;
|
||||
}
|
||||
|
||||
enum CharacterCodes {
|
||||
nullCharacter = 0,
|
||||
maxAsciiCharacter = 0x7F,
|
||||
|
@ -552,7 +588,7 @@ enum CharacterCodes {
|
|||
*/
|
||||
export function stripComments(text:string, replaceCh?:string):string {
|
||||
|
||||
var _scanner = createScanner(text),
|
||||
let _scanner = createScanner(text),
|
||||
parts: string[] = [],
|
||||
kind:SyntaxKind,
|
||||
offset = 0,
|
||||
|
@ -579,23 +615,415 @@ export function stripComments(text:string, replaceCh?:string):string {
|
|||
return parts.join('');
|
||||
}
|
||||
|
||||
export function parse(text:string, errors: string[] = []) : any {
|
||||
var noMatch = Object();
|
||||
var _scanner = createScanner(text, true);
|
||||
export interface ParseError {
|
||||
error: ParseErrorCode;
|
||||
}
|
||||
|
||||
function scanNext() : SyntaxKind {
|
||||
var token = _scanner.scan();
|
||||
while (token === SyntaxKind.Unknown) {
|
||||
handleError(nls.localize('UnknownSymbol', 'Invalid symbol'));
|
||||
token = _scanner.scan();
|
||||
export enum ParseErrorCode {
|
||||
InvalidSymbol,
|
||||
InvalidNumberFormat,
|
||||
PropertyNameExpected,
|
||||
ValueExpected,
|
||||
ColonExpected,
|
||||
CommaExpected,
|
||||
CloseBraceExpected,
|
||||
CloseBracketExpected,
|
||||
EndOfFileExpected
|
||||
}
|
||||
|
||||
export function getParseErrorMessage(errorCode: ParseErrorCode) : string {
|
||||
switch (errorCode) {
|
||||
case ParseErrorCode.InvalidSymbol: return localize('error.invalidSymbol', 'Invalid symbol');
|
||||
case ParseErrorCode.InvalidNumberFormat: return localize('error.invalidNumberFormat', 'Invalid number format');
|
||||
case ParseErrorCode.PropertyNameExpected: return localize('error.propertyNameExpected', 'Property name expected');
|
||||
case ParseErrorCode.ValueExpected: return localize('error.valueExpected', 'Value expected');
|
||||
case ParseErrorCode.ColonExpected: return localize('error.colonExpected', 'Colon expected');
|
||||
case ParseErrorCode.CommaExpected: return localize('error.commaExpected', 'Comma expected');
|
||||
case ParseErrorCode.CloseBraceExpected: return localize('error.closeBraceExpected', 'Closing brace expected');
|
||||
case ParseErrorCode.CloseBracketExpected: return localize('error.closeBracketExpected', 'Closing bracket expected');
|
||||
case ParseErrorCode.EndOfFileExpected: return localize('error.endOfFileExpected', 'End of file expected');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export type NodeType = "object" | "array" | "property" | "string" | "number" | "boolean" | "null";
|
||||
|
||||
function getLiteralNodeType(value: any) : NodeType {
|
||||
switch (typeof value) {
|
||||
case 'boolean': return 'boolean';
|
||||
case 'number': return 'number';
|
||||
case 'string': return 'string';
|
||||
default: return 'null';
|
||||
}
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
type: NodeType;
|
||||
value?: any;
|
||||
offset: number;
|
||||
length: number;
|
||||
columnOffset?: number;
|
||||
parent?: Node;
|
||||
children?: Node[];
|
||||
}
|
||||
|
||||
export type Segment = string | number;
|
||||
|
||||
export interface Location {
|
||||
/**
|
||||
* The previous property key or literal value (string, number, boolean or null) or undefined.
|
||||
*/
|
||||
previousNode?: Node;
|
||||
/**
|
||||
* The path describing the location in the JSON document. The path consists of a sequence strings
|
||||
* representing an object property or numbers for array indices.
|
||||
*/
|
||||
path: Segment[];
|
||||
/**
|
||||
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
|
||||
* '*' will match a single segment, of any property name or index.
|
||||
* '**' will match a sequece of segments or no segment, of any property name or index.
|
||||
*/
|
||||
matches: (patterns: Segment[]) => boolean;
|
||||
/**
|
||||
* If set, the location's offset is at a property key.
|
||||
*/
|
||||
isAtPropertyKey: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
|
||||
*/
|
||||
export function getLocation(text:string, position: number) : Location {
|
||||
let segments: any[] = []; // strings or numbers
|
||||
let earlyReturnException = new Object();
|
||||
let previousNode : Node = void 0;
|
||||
const previousNodeInst : Node = {
|
||||
value: void 0,
|
||||
offset: void 0,
|
||||
length: void 0,
|
||||
type: void 0
|
||||
};
|
||||
let isAtPropertyKey = false;
|
||||
function setPreviousNode(value: string, offset: number, length: number, type: NodeType) {
|
||||
previousNodeInst.value = value;
|
||||
previousNodeInst.offset = offset;
|
||||
previousNodeInst.length = length;
|
||||
previousNodeInst.type = type;
|
||||
previousNodeInst.columnOffset = void 0;
|
||||
previousNode = previousNodeInst;
|
||||
}
|
||||
try {
|
||||
|
||||
visit(text, {
|
||||
onObjectBegin: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
isAtPropertyKey = position > offset;
|
||||
segments.push(''); // push a placeholder (will be replaced or removed)
|
||||
},
|
||||
onObjectProperty: (name: string, offset: number, length: number) => {
|
||||
if (position < offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
setPreviousNode(name, offset, length, 'property');
|
||||
segments[segments.length - 1] = name;
|
||||
if (position <= offset + length) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
},
|
||||
onObjectEnd: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.pop();
|
||||
},
|
||||
onArrayBegin: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.push(0);
|
||||
},
|
||||
onArrayEnd: (offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
previousNode = void 0;
|
||||
segments.pop();
|
||||
},
|
||||
onLiteralValue: (value: any, offset: number, length: number) => {
|
||||
if (position < offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
setPreviousNode(value, offset, length, getLiteralNodeType(value));
|
||||
|
||||
if (position <= offset + length) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
},
|
||||
onSeparator: (sep: string, offset: number, length: number) => {
|
||||
if (position <= offset) {
|
||||
throw earlyReturnException;
|
||||
}
|
||||
if (sep === ':' && previousNode.type === 'property') {
|
||||
previousNode.columnOffset = offset;
|
||||
isAtPropertyKey = false;
|
||||
previousNode = void 0;
|
||||
} else if (sep === ',') {
|
||||
let last = segments[segments.length - 1];
|
||||
if (typeof last === 'number') {
|
||||
segments[segments.length - 1] = last + 1;
|
||||
} else {
|
||||
isAtPropertyKey = true;
|
||||
segments[segments.length - 1] = '';
|
||||
}
|
||||
previousNode = void 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e !== earlyReturnException) {
|
||||
throw e;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function handleError(message:string, skipUntilAfter: SyntaxKind[] = [], skipUntil: SyntaxKind[] = []) : void {
|
||||
errors.push(message);
|
||||
if (segments[segments.length - 1] === '') {
|
||||
segments.pop();
|
||||
}
|
||||
return {
|
||||
path: segments,
|
||||
previousNode,
|
||||
isAtPropertyKey,
|
||||
matches: (pattern: string[]) => {
|
||||
let k = 0;
|
||||
for (let i = 0; k < pattern.length && i < segments.length; i++) {
|
||||
if (pattern[k] === segments[i] || pattern[k] === '*') {
|
||||
k++;
|
||||
} else if (pattern[k] !== '**') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return k === pattern.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface ParseOptions {
|
||||
disallowComments?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
* Therefore always check the errors list to find out if the input was valid.
|
||||
*/
|
||||
export function parse(text:string, errors: ParseError[] = [], options?: ParseOptions) : any {
|
||||
let currentProperty : string = null;
|
||||
let currentParent : any = [];
|
||||
let previousParents : any[] = [];
|
||||
|
||||
function onValue(value: any) {
|
||||
if (Array.isArray(currentParent)) {
|
||||
(<any[]> currentParent).push(value);
|
||||
} else if (currentProperty) {
|
||||
currentParent[currentProperty] = value;
|
||||
}
|
||||
}
|
||||
|
||||
let visitor : JSONVisitor = {
|
||||
onObjectBegin: () => {
|
||||
let object = {};
|
||||
onValue(object);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = object;
|
||||
currentProperty = null;
|
||||
},
|
||||
onObjectProperty: (name: string) => {
|
||||
currentProperty = name;
|
||||
},
|
||||
onObjectEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onArrayBegin: () => {
|
||||
let array = [];
|
||||
onValue(array);
|
||||
previousParents.push(currentParent);
|
||||
currentParent = array;
|
||||
currentProperty = null;
|
||||
},
|
||||
onArrayEnd: () => {
|
||||
currentParent = previousParents.pop();
|
||||
},
|
||||
onLiteralValue: onValue,
|
||||
onError:(error:ParseErrorCode) => {
|
||||
errors.push({error: error});
|
||||
}
|
||||
};
|
||||
visit(text, visitor, options);
|
||||
return currentParent[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
||||
*/
|
||||
export function parseTree(text:string, errors: ParseError[] = [], options?: ParseOptions) : Node {
|
||||
let currentParent : Node = { type: 'array', offset: -1, length: -1, children: [] }; // artificial root
|
||||
|
||||
function ensurePropertyComplete(endOffset:number) {
|
||||
if (currentParent.type === 'property') {
|
||||
currentParent.length = endOffset - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
function onValue(valueNode: Node) : Node {
|
||||
currentParent.children.push(valueNode);
|
||||
ensurePropertyComplete(valueNode.offset + valueNode.length);
|
||||
return valueNode;
|
||||
}
|
||||
|
||||
let visitor : JSONVisitor = {
|
||||
onObjectBegin: (offset: number) => {
|
||||
currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });
|
||||
},
|
||||
onObjectProperty: (name: string, offset: number, length: number) => {
|
||||
currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });
|
||||
currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent});
|
||||
},
|
||||
onObjectEnd: (offset: number, length: number) => {
|
||||
ensurePropertyComplete(offset);
|
||||
currentParent.length = offset + length - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
},
|
||||
onArrayBegin: (offset: number, length: number) => {
|
||||
currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] });
|
||||
},
|
||||
onArrayEnd: (offset: number, length: number) => {
|
||||
currentParent.length = offset + length - currentParent.offset;
|
||||
currentParent = currentParent.parent;
|
||||
},
|
||||
onLiteralValue: (value: any, offset: number, length: number) => {
|
||||
onValue({ type: getLiteralNodeType(value), offset, length, parent: currentParent, value });
|
||||
},
|
||||
onSeparator: (sep: string, offset: number, length: number) => {
|
||||
if (currentParent.type === 'property') {
|
||||
if (sep === ':') {
|
||||
currentParent.columnOffset = offset;
|
||||
} else if (sep === ',') {
|
||||
ensurePropertyComplete(offset);
|
||||
}
|
||||
}
|
||||
},
|
||||
onError:(error:ParseErrorCode) => {
|
||||
errors.push({error: error});
|
||||
}
|
||||
};
|
||||
visit(text, visitor, options);
|
||||
|
||||
let result = currentParent.children[0];
|
||||
delete result.parent;
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findNodeAtLocation(root: Node, segments: Segment[]) : Node {
|
||||
let node = root;
|
||||
for (let segment of segments) {
|
||||
if (typeof segment === 'string') {
|
||||
if (node.type !== 'object') {
|
||||
return void 0;
|
||||
}
|
||||
let found = false;
|
||||
for (let propertyNode of node.children) {
|
||||
if (propertyNode.children[0].value === segment) {
|
||||
node = propertyNode.children[1];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return void 0;
|
||||
}
|
||||
} else {
|
||||
let index = <number> segment;
|
||||
if (node.type !== 'array' || index < 0 || index >= node.children.length) {
|
||||
return void 0;
|
||||
}
|
||||
node = node.children[index];
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getNodeValue(node: Node) : any {
|
||||
if (node.type === 'array') {
|
||||
return node.children.map(getNodeValue);
|
||||
} else if (node.type === 'object') {
|
||||
let obj = {};
|
||||
for (let prop of node.children) {
|
||||
obj[prop.children[0].value] = getNodeValue(prop.children[1]);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return node.value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
|
||||
*/
|
||||
export function visit(text:string, visitor: JSONVisitor, options?: ParseOptions) : any {
|
||||
|
||||
let _scanner = createScanner(text, false);
|
||||
|
||||
function toNoArgVisit(visitFunction: (offset: number, length: number) => void) : () => void {
|
||||
return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
}
|
||||
function toOneArgVisit<T>(visitFunction: (arg: T, offset: number, length: number) => void) : (arg: T) => void {
|
||||
return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true;
|
||||
}
|
||||
|
||||
let onObjectBegin = toNoArgVisit(visitor.onObjectBegin),
|
||||
onObjectProperty = toOneArgVisit(visitor.onObjectProperty),
|
||||
onObjectEnd = toNoArgVisit(visitor.onObjectEnd),
|
||||
onArrayBegin = toNoArgVisit(visitor.onArrayBegin),
|
||||
onArrayEnd = toNoArgVisit(visitor.onArrayEnd),
|
||||
onLiteralValue = toOneArgVisit(visitor.onLiteralValue),
|
||||
onSeparator = toOneArgVisit(visitor.onSeparator),
|
||||
onError = toOneArgVisit(visitor.onError);
|
||||
|
||||
let disallowComments = options && options.disallowComments;
|
||||
function scanNext() : SyntaxKind {
|
||||
while (true) {
|
||||
let token = _scanner.scan();
|
||||
switch (token) {
|
||||
case SyntaxKind.LineCommentTrivia:
|
||||
case SyntaxKind.BlockCommentTrivia:
|
||||
if (disallowComments) {
|
||||
handleError(ParseErrorCode.InvalidSymbol);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Unknown:
|
||||
handleError(ParseErrorCode.InvalidSymbol);
|
||||
break;
|
||||
case SyntaxKind.Trivia:
|
||||
case SyntaxKind.LineBreakTrivia:
|
||||
break;
|
||||
default:
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(error:ParseErrorCode, skipUntilAfter: SyntaxKind[] = [], skipUntil: SyntaxKind[] = []) : void {
|
||||
onError(error);
|
||||
if (skipUntilAfter.length + skipUntil.length > 0) {
|
||||
var token = _scanner.getToken();
|
||||
let token = _scanner.getToken();
|
||||
while (token !== SyntaxKind.EOF) {
|
||||
if (skipUntilAfter.indexOf(token) !== -1) {
|
||||
scanNext();
|
||||
|
@ -608,156 +1036,186 @@ export function parse(text:string, errors: string[] = []) : any {
|
|||
}
|
||||
}
|
||||
|
||||
function parseString() : any {
|
||||
function parseString(isValue: boolean) : boolean {
|
||||
if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
|
||||
return noMatch;
|
||||
return false;
|
||||
}
|
||||
let value = _scanner.getTokenValue();
|
||||
if (isValue) {
|
||||
onLiteralValue(value);
|
||||
} else {
|
||||
onObjectProperty(value);
|
||||
}
|
||||
var value = _scanner.getTokenValue();
|
||||
scanNext();
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseLiteral() : any {
|
||||
var value : any;
|
||||
function parseLiteral() : boolean {
|
||||
switch (_scanner.getToken()) {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
let value = 0;
|
||||
try {
|
||||
value = JSON.parse(_scanner.getTokenValue());
|
||||
if (typeof value !== 'number') {
|
||||
handleError(nls.localize('InvalidNumberFormat', 'Invalid number format'));
|
||||
handleError(ParseErrorCode.InvalidNumberFormat);
|
||||
value = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
value = 0;
|
||||
handleError(ParseErrorCode.InvalidNumberFormat);
|
||||
}
|
||||
onLiteralValue(value);
|
||||
break;
|
||||
case SyntaxKind.NullKeyword:
|
||||
value = null;
|
||||
onLiteralValue(null);
|
||||
break;
|
||||
case SyntaxKind.TrueKeyword:
|
||||
value = true;
|
||||
onLiteralValue(true);
|
||||
break;
|
||||
case SyntaxKind.FalseKeyword:
|
||||
value = false;
|
||||
onLiteralValue(false);
|
||||
break;
|
||||
default:
|
||||
return noMatch;
|
||||
return false;
|
||||
}
|
||||
scanNext();
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseProperty(result: any) : any {
|
||||
var key = parseString();
|
||||
if (key === noMatch) {
|
||||
handleError(nls.localize('PropertyExpected', 'Property name expected'), [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
function parseProperty() : boolean {
|
||||
if (!parseString(false)) {
|
||||
handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
return false;
|
||||
}
|
||||
if (_scanner.getToken() === SyntaxKind.ColonToken) {
|
||||
onSeparator(':');
|
||||
scanNext(); // consume colon
|
||||
|
||||
var value = parseValue();
|
||||
if (value !== noMatch) {
|
||||
result[key] = value;
|
||||
} else {
|
||||
handleError(nls.localize('ValueExpected', 'Value expected'), [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
if (!parseValue()) {
|
||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
}
|
||||
} else {
|
||||
handleError(nls.localize('ColonExpected', 'Colon expected'), [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
handleError(ParseErrorCode.ColonExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseObject() : any {
|
||||
function parseObject() : boolean {
|
||||
if (_scanner.getToken() !== SyntaxKind.OpenBraceToken) {
|
||||
return noMatch;
|
||||
return false;
|
||||
}
|
||||
var obj = {};
|
||||
onObjectBegin();
|
||||
scanNext(); // consume open brace
|
||||
|
||||
var needsComma = false;
|
||||
let needsComma = false;
|
||||
while (_scanner.getToken() !== SyntaxKind.CloseBraceToken && _scanner.getToken() !== SyntaxKind.EOF) {
|
||||
if (_scanner.getToken() === SyntaxKind.CommaToken) {
|
||||
if (!needsComma) {
|
||||
handleError(nls.localize('ValueExpected', 'Value expected'), [], [] );
|
||||
handleError(ParseErrorCode.ValueExpected, [], [] );
|
||||
}
|
||||
onSeparator(',');
|
||||
scanNext(); // consume comma
|
||||
} else if (needsComma) {
|
||||
handleError(nls.localize('CommaExpected', 'Comma expected'), [], [] );
|
||||
handleError(ParseErrorCode.CommaExpected, [], [] );
|
||||
}
|
||||
var propertyParsed = parseProperty(obj);
|
||||
if (!propertyParsed) {
|
||||
handleError(nls.localize('ValueExpected', 'Value expected'), [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
if (!parseProperty()) {
|
||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken] );
|
||||
}
|
||||
needsComma = true;
|
||||
}
|
||||
|
||||
onObjectEnd();
|
||||
if (_scanner.getToken() !== SyntaxKind.CloseBraceToken) {
|
||||
handleError(nls.localize('CloseBraceExpected', 'Closing brace expected'), [SyntaxKind.CloseBraceToken], []);
|
||||
handleError(ParseErrorCode.CloseBraceExpected, [SyntaxKind.CloseBraceToken], []);
|
||||
} else {
|
||||
scanNext(); // consume close brace
|
||||
}
|
||||
return obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseArray() : any {
|
||||
function parseArray() : boolean {
|
||||
if (_scanner.getToken() !== SyntaxKind.OpenBracketToken) {
|
||||
return noMatch;
|
||||
return false;
|
||||
}
|
||||
var arr: any[] = [];
|
||||
onArrayBegin();
|
||||
scanNext(); // consume open bracket
|
||||
|
||||
var needsComma = false;
|
||||
let needsComma = false;
|
||||
while (_scanner.getToken() !== SyntaxKind.CloseBracketToken && _scanner.getToken() !== SyntaxKind.EOF) {
|
||||
if (_scanner.getToken() === SyntaxKind.CommaToken) {
|
||||
if (!needsComma) {
|
||||
handleError(nls.localize('ValeExpected', 'Value expected'), [], [] );
|
||||
handleError(ParseErrorCode.ValueExpected, [], [] );
|
||||
}
|
||||
onSeparator(',');
|
||||
scanNext(); // consume comma
|
||||
} else if (needsComma) {
|
||||
handleError(nls.localize('CommaExpected', 'Comma expected'), [], [] );
|
||||
handleError(ParseErrorCode.CommaExpected, [], [] );
|
||||
}
|
||||
var value = parseValue();
|
||||
if (value === noMatch) {
|
||||
handleError(nls.localize('ValueExpected', 'Value expected'), [], [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken] );
|
||||
} else {
|
||||
arr.push(value);
|
||||
if (!parseValue()) {
|
||||
handleError(ParseErrorCode.ValueExpected, [], [SyntaxKind.CloseBracketToken, SyntaxKind.CommaToken] );
|
||||
}
|
||||
needsComma = true;
|
||||
}
|
||||
|
||||
onArrayEnd();
|
||||
if (_scanner.getToken() !== SyntaxKind.CloseBracketToken) {
|
||||
handleError(nls.localize('CloseBracketExpected', 'Closing bracket expected'), [SyntaxKind.CloseBracketToken], []);
|
||||
handleError(ParseErrorCode.CloseBracketExpected, [SyntaxKind.CloseBracketToken], []);
|
||||
} else {
|
||||
scanNext(); // consume close bracket
|
||||
}
|
||||
return arr;
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseValue() : any {
|
||||
var result = parseArray();
|
||||
if (result !== noMatch) {
|
||||
return result;
|
||||
}
|
||||
result = parseObject();
|
||||
if (result !== noMatch) {
|
||||
return result;
|
||||
}
|
||||
result = parseString();
|
||||
if (result !== noMatch) {
|
||||
return result;
|
||||
}
|
||||
return parseLiteral();
|
||||
function parseValue() : boolean {
|
||||
return parseArray() || parseObject() || parseString(true) || parseLiteral();
|
||||
}
|
||||
|
||||
scanNext();
|
||||
var value = parseValue();
|
||||
if (value === noMatch) {
|
||||
handleError(nls.localize('ValueExpected', 'Value expected'), [], []);
|
||||
return void 0;
|
||||
if (!parseValue()) {
|
||||
handleError(ParseErrorCode.ValueExpected, [], []);
|
||||
return false;
|
||||
}
|
||||
if (_scanner.getToken() !== SyntaxKind.EOF) {
|
||||
handleError(nls.localize('EOFExpected', 'End of content expected'), [], []);
|
||||
handleError(ParseErrorCode.EndOfFileExpected, [], []);
|
||||
}
|
||||
return value;
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface JSONVisitor {
|
||||
/**
|
||||
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
|
||||
*/
|
||||
onObjectBegin?: (offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a property is encountered. The offset and length represent the location of the property name.
|
||||
*/
|
||||
onObjectProperty?: (property: string, offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
|
||||
*/
|
||||
onObjectEnd?: (offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
|
||||
*/
|
||||
onArrayBegin?: (offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
|
||||
*/
|
||||
onArrayEnd?: (offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
|
||||
*/
|
||||
onLiteralValue?: (value: any, offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
|
||||
*/
|
||||
onSeparator?: (charcter: string, offset:number, length:number) => void;
|
||||
|
||||
/**
|
||||
* Invoked on an error.
|
||||
*/
|
||||
onError?: (error: ParseErrorCode, offset:number, length:number) => void;
|
||||
}
|
||||
|
|
94
src/vs/base/common/jsonEdit.ts
Normal file
94
src/vs/base/common/jsonEdit.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ParseError, parseTree, Segment, findNodeAtLocation } from 'vs/base/common/json';
|
||||
import { Edit, FormattingOptions, format } from 'vs/base/common/jsonFormatter';
|
||||
|
||||
export function removeProperty(text: string, segments: Segment[], formattingOptions: FormattingOptions) : Edit[] {
|
||||
return setProperty(text, segments, void 0, formattingOptions);
|
||||
}
|
||||
|
||||
export function setProperty(text: string, segments: Segment[], value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number) : Edit[] {
|
||||
let lastSegment = segments.pop();
|
||||
if (typeof lastSegment !== 'string') {
|
||||
throw new Error('Last segment must be a property name');
|
||||
}
|
||||
|
||||
let errors: ParseError[] = [];
|
||||
let node = parseTree(text, errors);
|
||||
if (segments.length > 0) {
|
||||
node = findNodeAtLocation(node, segments);
|
||||
if (node === void 0) {
|
||||
throw new Error('Cannot find object');
|
||||
}
|
||||
}
|
||||
if (node && node.type === 'object') {
|
||||
let existing = findNodeAtLocation(node, [ lastSegment ]);
|
||||
if (existing !== void 0) {
|
||||
if (value === void 0) { // delete
|
||||
let propertyIndex = node.children.indexOf(existing.parent);
|
||||
let removeBegin : number;
|
||||
let removeEnd = existing.parent.offset + existing.parent.length;
|
||||
if (propertyIndex > 0) {
|
||||
// remove the comma of the previous node
|
||||
let previous = node.children[propertyIndex - 1];
|
||||
removeBegin = previous.offset + previous.length;
|
||||
} else {
|
||||
removeBegin = node.offset + 1;
|
||||
if (node.children.length > 1) {
|
||||
// remove the comma of the next node
|
||||
let next = node.children[1];
|
||||
removeEnd = next.offset;
|
||||
}
|
||||
}
|
||||
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, formattingOptions);
|
||||
} else {
|
||||
// set value of existing property
|
||||
return [{ offset: existing.offset, length: existing.length, content: JSON.stringify(value) }];
|
||||
}
|
||||
} else {
|
||||
if (value === void 0) { // delete
|
||||
throw new Error(`Property ${lastSegment} does not exist.`);
|
||||
}
|
||||
let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
|
||||
let index = getInsertionIndex ? getInsertionIndex(node.children.map(p => p.children[0].value)) : node.children.length;
|
||||
let edit: Edit;
|
||||
if (index > 0) {
|
||||
let previous = node.children[index - 1];
|
||||
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty};
|
||||
} else if (node.children.length === 0) {
|
||||
edit = { offset: node.offset + 1, length: 0, content: newProperty};
|
||||
} else {
|
||||
edit = { offset: node.offset + 1, length: 0, content: newProperty + ','};
|
||||
}
|
||||
return withFormatting(text, edit, formattingOptions);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Path does not reference an object');
|
||||
}
|
||||
}
|
||||
|
||||
function withFormatting(text:string, edit: Edit, formattingOptions: FormattingOptions) : Edit[] {
|
||||
// apply the edit
|
||||
let newText = text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
|
||||
|
||||
// format the new text
|
||||
let begin = edit.offset;
|
||||
let end = edit.offset + edit.content.length;
|
||||
let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions);
|
||||
|
||||
// apply the formatting edits and track the begin and end offsets of the changes
|
||||
for (let i = edits.length - 1; i >= 0; i--) {
|
||||
let edit = edits[i];
|
||||
newText = newText.substring(0, edit.offset) + edit.content + newText.substring(edit.offset + edit.length);
|
||||
begin = Math.min(begin, edit.offset);
|
||||
end = Math.max(end, edit.offset + edit.length);
|
||||
end += edit.content.length - edit.length;
|
||||
}
|
||||
// create a single edit with all changes
|
||||
let editLength = text.length - (newText.length - end) - begin;
|
||||
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
|
||||
}
|
203
src/vs/base/common/jsonFormatter.ts
Normal file
203
src/vs/base/common/jsonFormatter.ts
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 Json = require('./json');
|
||||
|
||||
export interface FormattingOptions {
|
||||
/**
|
||||
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent?
|
||||
*/
|
||||
tabSize: number;
|
||||
/**
|
||||
* Is indentation based on spaces?
|
||||
*/
|
||||
insertSpaces: boolean;
|
||||
/**
|
||||
* The default end of line line character
|
||||
*/
|
||||
eol: string;
|
||||
}
|
||||
|
||||
export interface Edit {
|
||||
offset: number;
|
||||
length: number;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function format(documentText: string, range: { offset: number, length: number}, options: FormattingOptions): Edit[] {
|
||||
let initialIndentLevel: number;
|
||||
let value: string;
|
||||
let rangeStart: number;
|
||||
let rangeEnd: number;
|
||||
if (range) {
|
||||
rangeStart = range.offset;
|
||||
rangeEnd = rangeStart + range.length;
|
||||
while (rangeStart > 0 && !isEOL(documentText, rangeStart - 1)) {
|
||||
rangeStart--;
|
||||
}
|
||||
let scanner = Json.createScanner(documentText, true);
|
||||
scanner.setPosition(rangeEnd);
|
||||
scanner.scan();
|
||||
rangeEnd = scanner.getPosition();
|
||||
|
||||
value = documentText.substring(rangeStart, rangeEnd);
|
||||
initialIndentLevel = computeIndentLevel(value, 0, options);
|
||||
} else {
|
||||
value = documentText;
|
||||
rangeStart = 0;
|
||||
rangeEnd = documentText.length;
|
||||
initialIndentLevel = 0;
|
||||
}
|
||||
let eol = getEOL(options, documentText);
|
||||
|
||||
let lineBreak = false;
|
||||
let indentLevel = 0;
|
||||
let indentValue: string;
|
||||
if (options.insertSpaces) {
|
||||
indentValue = repeat(' ', options.tabSize);
|
||||
} else {
|
||||
indentValue = '\t';
|
||||
}
|
||||
|
||||
let scanner = Json.createScanner(value, false);
|
||||
|
||||
function newLineAndIndent(): string {
|
||||
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
|
||||
}
|
||||
function scanNext(): Json.SyntaxKind {
|
||||
let token = scanner.scan();
|
||||
lineBreak = false;
|
||||
while (token === Json.SyntaxKind.Trivia || token === Json.SyntaxKind.LineBreakTrivia) {
|
||||
lineBreak = lineBreak || (token === Json.SyntaxKind.LineBreakTrivia);
|
||||
token = scanner.scan();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
let editOperations: Edit[] = [];
|
||||
function addEdit(text: string, startOffset: number, endOffset: number) {
|
||||
if (documentText.substring(startOffset, endOffset) !== text) {
|
||||
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
|
||||
}
|
||||
}
|
||||
|
||||
let firstToken = scanNext();
|
||||
if (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
let initialIndent = repeat(indentValue, initialIndentLevel);
|
||||
addEdit(initialIndent, rangeStart, firstTokenStart);
|
||||
}
|
||||
|
||||
while (firstToken !== Json.SyntaxKind.EOF) {
|
||||
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart;
|
||||
let secondToken = scanNext();
|
||||
|
||||
let replaceContent = '';
|
||||
while (!lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
// comments on the same line: keep them on the same line, but ignore them otherwise
|
||||
let commentTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
addEdit(' ', firstTokenEnd, commentTokenStart);
|
||||
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + rangeStart;
|
||||
replaceContent = secondToken === Json.SyntaxKind.LineCommentTrivia ? newLineAndIndent() : '';
|
||||
secondToken = scanNext();
|
||||
}
|
||||
|
||||
if (secondToken === Json.SyntaxKind.CloseBraceToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBraceToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else if (secondToken === Json.SyntaxKind.CloseBracketToken) {
|
||||
if (firstToken !== Json.SyntaxKind.OpenBracketToken) {
|
||||
indentLevel--;
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
} else if (secondToken !== Json.SyntaxKind.EOF) {
|
||||
switch (firstToken) {
|
||||
case Json.SyntaxKind.OpenBracketToken:
|
||||
case Json.SyntaxKind.OpenBraceToken:
|
||||
indentLevel++;
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.CommaToken:
|
||||
case Json.SyntaxKind.LineCommentTrivia:
|
||||
replaceContent = newLineAndIndent();
|
||||
break;
|
||||
case Json.SyntaxKind.BlockCommentTrivia:
|
||||
if (lineBreak) {
|
||||
replaceContent = newLineAndIndent();
|
||||
} else {
|
||||
// symbol following comment on the same line: keep on same line, separate with ' '
|
||||
replaceContent = ' ';
|
||||
}
|
||||
break;
|
||||
case Json.SyntaxKind.ColonToken:
|
||||
replaceContent = ' ';
|
||||
break;
|
||||
case Json.SyntaxKind.NullKeyword:
|
||||
case Json.SyntaxKind.TrueKeyword:
|
||||
case Json.SyntaxKind.FalseKeyword:
|
||||
case Json.SyntaxKind.NumericLiteral:
|
||||
if (secondToken === Json.SyntaxKind.NullKeyword || secondToken === Json.SyntaxKind.FalseKeyword || secondToken === Json.SyntaxKind.NumericLiteral) {
|
||||
replaceContent = ' ';
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lineBreak && (secondToken === Json.SyntaxKind.LineCommentTrivia || secondToken === Json.SyntaxKind.BlockCommentTrivia)) {
|
||||
replaceContent = newLineAndIndent();
|
||||
}
|
||||
|
||||
}
|
||||
let secondTokenStart = scanner.getTokenOffset() + rangeStart;
|
||||
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
|
||||
firstToken = secondToken;
|
||||
}
|
||||
return editOperations;
|
||||
}
|
||||
|
||||
function repeat(s: string, count: number): string {
|
||||
let result = '';
|
||||
for (let i = 0; i < count; i++) {
|
||||
result += s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function computeIndentLevel(content: string, offset: number, options: FormattingOptions): number {
|
||||
let i = 0;
|
||||
let nChars = 0;
|
||||
let tabSize = options.tabSize || 4;
|
||||
while (i < content.length) {
|
||||
let ch = content.charAt(i);
|
||||
if (ch === ' ') {
|
||||
nChars++;
|
||||
} else if (ch === '\t') {
|
||||
nChars += tabSize;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return Math.floor(nChars / tabSize);
|
||||
}
|
||||
|
||||
function getEOL(options: FormattingOptions, text: string): string {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let ch = text.charAt(i);
|
||||
if (ch === '\r') {
|
||||
if (i + 1 < text.length && text.charAt(i+1) === '\n') {
|
||||
return '\r\n';
|
||||
}
|
||||
return '\r';
|
||||
} else if (ch === '\n') {
|
||||
return '\n';
|
||||
}
|
||||
}
|
||||
return (options && options.eol) || '\n';
|
||||
}
|
||||
|
||||
function isEOL(text: string, offset: number) {
|
||||
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
|
||||
}
|
|
@ -83,7 +83,15 @@ export abstract class Disposable implements IDisposable {
|
|||
|
||||
export class Disposables extends Disposable {
|
||||
|
||||
public add(disposable: IDisposable): void {
|
||||
this._register(disposable);
|
||||
public add<T extends IDisposable>(e: T): T;
|
||||
public add(...elements: IDisposable[]): void;
|
||||
public add<T extends IDisposable>(arg: T | T[]): T {
|
||||
if (!Array.isArray(arg)) {
|
||||
return this._register(arg);
|
||||
} else {
|
||||
for (let element of arg) {
|
||||
return this._register(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,14 +151,6 @@ export function toObject<T,R>(arr: T[], keyMap: (T) => string, valueMap: (T) =>
|
|||
return arr.reduce((o, d) => assign(o, { [keyMap(d)]: valueMap(d) }), Object.create(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object that has all values of {{obj}}
|
||||
* plus those from {{defaults}}.
|
||||
*/
|
||||
export function withDefaults<T>(obj: T, defaults: T): T {
|
||||
return mixin(clone(defaults), obj || {});
|
||||
}
|
||||
|
||||
export function equals(one: any, other: any): boolean {
|
||||
if (one === other) {
|
||||
return true;
|
||||
|
|
|
@ -6,6 +6,14 @@
|
|||
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
|
||||
const _typeof = {
|
||||
number: 'number',
|
||||
string: 'string',
|
||||
undefined: 'undefined',
|
||||
object: 'object',
|
||||
function: 'function'
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array or not.
|
||||
*/
|
||||
|
@ -14,7 +22,7 @@ export function isArray(array: any): array is any[] {
|
|||
return Array.isArray(array);
|
||||
}
|
||||
|
||||
if (array && typeof (array.length) === 'number' && array.constructor === Array) {
|
||||
if (array && typeof (array.length) === _typeof.number && array.constructor === Array) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,7 +33,7 @@ export function isArray(array: any): array is any[] {
|
|||
* @returns whether the provided parameter is a JavaScript String or not.
|
||||
*/
|
||||
export function isString(str: any): str is string {
|
||||
if (typeof (str) === 'string' || str instanceof String) {
|
||||
if (typeof (str) === _typeof.string|| str instanceof String) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,23 +48,24 @@ export function isStringArray(value: any): value is string[] {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Object or not.
|
||||
*
|
||||
* @returns whether the provided parameter is of type `object` but **not**
|
||||
* `null`, an `array`, a `regexp`, nor a `date`.
|
||||
*/
|
||||
export function isObject(obj: any): obj is any {
|
||||
|
||||
// Needed for IE8
|
||||
if (typeof obj === 'undefined' || obj === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object.prototype.toString.call(obj) === '[object Object]';
|
||||
return typeof obj === _typeof.object
|
||||
&& obj !== null
|
||||
&& !Array.isArray(obj)
|
||||
&& !(obj instanceof RegExp)
|
||||
&& !(obj instanceof Date);
|
||||
}
|
||||
|
||||
/**
|
||||
* In **contrast** to just checking `typeof` this will return `false` for `NaN`.
|
||||
* @returns whether the provided parameter is a JavaScript Number or not.
|
||||
*/
|
||||
export function isNumber(obj: any): obj is number {
|
||||
if ((typeof (obj) === 'number' || obj instanceof Number) && !isNaN(obj)) {
|
||||
if ((typeof (obj) === _typeof.number || obj instanceof Number) && !isNaN(obj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -74,7 +83,7 @@ export function isBoolean(obj: any): obj is boolean {
|
|||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): boolean {
|
||||
return typeof (obj) === 'undefined';
|
||||
return typeof (obj) === _typeof.undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,14 +117,14 @@ export function isEmptyObject(obj: any): obj is any {
|
|||
* @returns whether the provided parameter is a JavaScript Function or not.
|
||||
*/
|
||||
export function isFunction(obj: any): obj is Function {
|
||||
return Object.prototype.toString.call(obj) === '[object Function]';
|
||||
return typeof obj === _typeof.function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameters is are JavaScript Function or not.
|
||||
*/
|
||||
export function areFunctions(...objects: any[]): boolean {
|
||||
return objects && objects.length > 0 && objects.every((object) => isFunction(object));
|
||||
return objects && objects.length > 0 && objects.every(isFunction);
|
||||
}
|
||||
|
||||
export type TypeConstraint = string | Function;
|
||||
|
@ -129,11 +138,11 @@ export function validateConstraints(args: any[], constraints: TypeConstraint[]):
|
|||
|
||||
export function validateConstraint(arg: any, constraint: TypeConstraint): void {
|
||||
|
||||
if (typeof constraint === 'string') {
|
||||
if (isString(constraint)) {
|
||||
if (typeof arg !== constraint) {
|
||||
throw new Error(`argument does not match constraint: typeof ${constraint}`);
|
||||
}
|
||||
} else if (typeof constraint === 'function') {
|
||||
} else if (isFunction(constraint)) {
|
||||
if (arg instanceof constraint) {
|
||||
return;
|
||||
}
|
||||
|
|
1
src/vs/base/common/winjs.base.d.ts
vendored
1
src/vs/base/common/winjs.base.d.ts
vendored
|
@ -95,6 +95,7 @@ export declare class TPromise<V> {
|
|||
public static is(value: any): value is TPromise<any>;
|
||||
public static timeout(delay:number):TPromise<void>;
|
||||
public static join<ValueType>(promises:TPromise<ValueType>[]):TPromise<ValueType[]>;
|
||||
public static join<ValueType>(promises:Thenable<ValueType>[]):Thenable<ValueType[]>;
|
||||
public static join<ValueType>(promises: {[n:string]:TPromise<ValueType>}):TPromise<{[n:string]:ValueType}>;
|
||||
public static any<ValueType>(promises:TPromise<ValueType>[]):TPromise<{ key:string; value:TPromise<ValueType>;}>;
|
||||
public static wrapError<ValueType>(error:any):TPromise<ValueType>;
|
||||
|
|
|
@ -157,9 +157,10 @@ class SimpleWorkerProtocol {
|
|||
export class SimpleWorkerClient<T> extends Disposable {
|
||||
|
||||
private _worker:IWorker;
|
||||
private _onModuleLoaded:TPromise<void>;
|
||||
private _onModuleLoaded:TPromise<string[]>;
|
||||
private _protocol: SimpleWorkerProtocol;
|
||||
private _proxy: T;
|
||||
private _lazyProxy: TPromise<T>;
|
||||
private _lastRequestTimestamp = -1;
|
||||
|
||||
constructor(workerFactory:IWorkerFactory, moduleId:string, ctor:any) {
|
||||
|
@ -190,13 +191,30 @@ export class SimpleWorkerClient<T> extends Disposable {
|
|||
loaderConfiguration = (<any>window).requirejs.s.contexts._.config;
|
||||
}
|
||||
|
||||
let lazyProxyFulfill : (v:T)=>void = null;
|
||||
let lazyProxyReject: (err:any)=>void = null;
|
||||
|
||||
this._lazyProxy = new TPromise((c, e, p) => {
|
||||
lazyProxyFulfill = c;
|
||||
lazyProxyReject = e;
|
||||
}, () => { /* no cancel */ });
|
||||
|
||||
// Send initialize message
|
||||
this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [
|
||||
this._worker.getId(),
|
||||
moduleId,
|
||||
loaderConfiguration
|
||||
]);
|
||||
this._onModuleLoaded.then(null, (e) => this._onError('Worker failed to load ' + moduleId, e));
|
||||
this._onModuleLoaded.then((availableMethods:string[]) => {
|
||||
let proxy = <T><any>{};
|
||||
for (let i = 0; i < availableMethods.length; i++) {
|
||||
proxy[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest);
|
||||
}
|
||||
lazyProxyFulfill(proxy);
|
||||
}, (e) => {
|
||||
lazyProxyReject(e);
|
||||
this._onError('Worker failed to load ' + moduleId, e);
|
||||
});
|
||||
|
||||
// Create proxy to loaded code
|
||||
let proxyMethodRequest = (method:string, args:any[]):TPromise<any> => {
|
||||
|
@ -211,10 +229,13 @@ export class SimpleWorkerClient<T> extends Disposable {
|
|||
};
|
||||
|
||||
this._proxy = <T><any>{};
|
||||
for (let prop in ctor.prototype) {
|
||||
if (ctor.prototype.hasOwnProperty(prop)) {
|
||||
if (typeof ctor.prototype[prop] === 'function') {
|
||||
this._proxy[prop] = createProxyMethod(prop, proxyMethodRequest);
|
||||
if (ctor) {
|
||||
// console.warn('deprecated');
|
||||
for (let prop in ctor.prototype) {
|
||||
if (ctor.prototype.hasOwnProperty(prop)) {
|
||||
if (typeof ctor.prototype[prop] === 'function') {
|
||||
this._proxy[prop] = createProxyMethod(prop, proxyMethodRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +245,10 @@ export class SimpleWorkerClient<T> extends Disposable {
|
|||
return this._proxy;
|
||||
}
|
||||
|
||||
public getProxyObject(): TPromise<T> {
|
||||
return this._lazyProxy;
|
||||
}
|
||||
|
||||
public getLastRequestTimestamp(): number {
|
||||
return this._lastRequestTimestamp;
|
||||
}
|
||||
|
@ -323,7 +348,15 @@ export class SimpleWorkerServer {
|
|||
require([moduleId], (...result:any[]) => {
|
||||
let handlerModule = result[0];
|
||||
this._requestHandler = handlerModule.create();
|
||||
cc(null);
|
||||
|
||||
let methods: string[] = [];
|
||||
for (let prop in this._requestHandler) {
|
||||
if (typeof this._requestHandler[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
cc(methods);
|
||||
}, ee);
|
||||
|
||||
return r;
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import types = require('vs/base/common/types');
|
||||
import {safeStringify, mixin} from 'vs/base/common/objects';
|
||||
|
||||
import appInsights = require('applicationinsights');
|
||||
|
||||
export interface IAIAdapter {
|
||||
log(eventName: string, data?: any): void;
|
||||
logException(exception: any): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class AIAdapter implements IAIAdapter {
|
||||
|
||||
private appInsights: typeof appInsights.client;
|
||||
|
||||
constructor(
|
||||
private aiKey: string,
|
||||
private eventPrefix: string,
|
||||
/* for test only */
|
||||
client?: any,
|
||||
private additionalDataToLog?: any
|
||||
) {
|
||||
// for test
|
||||
if (client) {
|
||||
this.appInsights = client;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aiKey) {
|
||||
// if another client is already initialized
|
||||
if (appInsights.client) {
|
||||
this.appInsights = appInsights.getClient(aiKey);
|
||||
// no other way to enable offline mode
|
||||
this.appInsights.channel.setOfflineMode(true);
|
||||
|
||||
} else {
|
||||
this.appInsights = appInsights.setup(aiKey)
|
||||
.setAutoCollectRequests(false)
|
||||
.setAutoCollectPerformance(false)
|
||||
.setAutoCollectExceptions(false)
|
||||
.setOfflineMode(true)
|
||||
.start()
|
||||
.client;
|
||||
}
|
||||
|
||||
|
||||
if(aiKey.indexOf('AIF-') === 0) {
|
||||
this.appInsights.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
|
||||
}
|
||||
|
||||
this.setupAIClient(this.appInsights);
|
||||
}
|
||||
}
|
||||
|
||||
private setupAIClient(client: typeof appInsights.client): void {
|
||||
//prevent App Insights from reporting machine name
|
||||
if (client && client.context &&
|
||||
client.context.keys && client.context.tags) {
|
||||
var machineNameKey = client.context.keys.deviceMachineName;
|
||||
client.context.tags[machineNameKey] = '';
|
||||
}
|
||||
}
|
||||
|
||||
private getData(data?: any): any {
|
||||
var properties: {[key: string]: string;} = {};
|
||||
var measurements: {[key: string]: number;} = {};
|
||||
|
||||
var event_data = this.flaten(data);
|
||||
for(var prop in event_data) {
|
||||
// enforce property names less than 150 char, take the last 150 char
|
||||
var propName = prop && prop.length > 150 ? prop.substr( prop.length - 149) : prop;
|
||||
var property = event_data[prop];
|
||||
if (types.isNumber(property)) {
|
||||
measurements[propName] = property;
|
||||
|
||||
} else if (types.isBoolean(property)) {
|
||||
measurements[propName] = property ? 1:0;
|
||||
} else if (types.isString(property)) {
|
||||
//enforce proeprty value to be less than 1024 char, take the first 1024 char
|
||||
var propValue = property && property.length > 1024 ? property.substring(0, 1023): property;
|
||||
properties[propName] = propValue;
|
||||
} else if (!types.isUndefined(property) && property !== null) {
|
||||
properties[propName] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
properties: properties,
|
||||
measurements: measurements
|
||||
};
|
||||
}
|
||||
|
||||
private flaten(obj:any, order:number = 0, prefix? : string): any {
|
||||
var result:{[key:string]: any} = {};
|
||||
var properties = obj ? Object.getOwnPropertyNames(obj) : [];
|
||||
for (var i =0; i < properties.length; i++) {
|
||||
var item = properties[i];
|
||||
var index = prefix ? prefix + item : item;
|
||||
|
||||
if (types.isArray(obj[item])) {
|
||||
try {
|
||||
result[index] = safeStringify(obj[item]);
|
||||
} catch (e) {
|
||||
// workaround for catching the edge case for #18383
|
||||
// safe stringfy should never throw circular object exception
|
||||
result[index] = '[Circular-Array]';
|
||||
}
|
||||
} else if (obj[item] instanceof Date) {
|
||||
result[index] = (<Date> obj[item]).toISOString();
|
||||
} else if (types.isObject(obj[item])) {
|
||||
if (order < 2) {
|
||||
var item_result = this.flaten(obj[item], order + 1, index + '.');
|
||||
for (var prop in item_result) {
|
||||
result[prop] = item_result[prop];
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
result[index] = safeStringify(obj[item]);
|
||||
} catch (e) {
|
||||
// workaround for catching the edge case for #18383
|
||||
// safe stringfy should never throw circular object exception
|
||||
result[index] = '[Circular]';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[index] = obj[item];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public log(eventName: string, data?: any): void {
|
||||
if (this.additionalDataToLog) {
|
||||
data = mixin(data, this.additionalDataToLog);
|
||||
}
|
||||
var result = this.getData(data);
|
||||
|
||||
if (this.appInsights) {
|
||||
this.appInsights.trackEvent(this.eventPrefix+'/'+eventName, result.properties, result.measurements);
|
||||
}
|
||||
}
|
||||
|
||||
public logException(exception: any): void {
|
||||
if (this.appInsights) {
|
||||
this.appInsights.trackException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.appInsights = null;
|
||||
}
|
||||
}
|
16
src/vs/base/node/paths.ts
Normal file
16
src/vs/base/node/paths.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import uri from 'vs/base/common/uri';
|
||||
|
||||
interface IPaths {
|
||||
getAppDataPath(platform: string): string;
|
||||
getUserDataPath(platform: string, appName: string, args: string[]): string;
|
||||
}
|
||||
|
||||
const pathsPath = uri.parse(require.toUrl('paths')).fsPath;
|
||||
const paths = require.__$__nodeRequire<IPaths>(pathsPath);
|
||||
export const getAppDataPath = paths.getAppDataPath;
|
||||
export const getUserDataPath = paths.getUserDataPath;
|
|
@ -25,7 +25,7 @@ export function findFreePort(startPort: number, giveUpAfter: number, timeout: nu
|
|||
doFindFreePort(startPort, giveUpAfter, (port) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
window.clearInterval(timeoutHandle);
|
||||
window.clearTimeout(timeoutHandle);
|
||||
|
||||
return clb(port);
|
||||
}
|
||||
|
|
52
src/vs/base/parts/ai/node/ai.app.ts
Normal file
52
src/vs/base/parts/ai/node/ai.app.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 {IServer} from 'vs/base/parts/ipc/common/ipc';
|
||||
import {AIAdapter} from './aiAdapter';
|
||||
import {IAIChannel} from './ai.ipc';
|
||||
|
||||
const adapter: { [handle: number]: AIAdapter } = Object.create(null);
|
||||
let idPool = 0;
|
||||
|
||||
export function registerAIChannel(server: IServer) {
|
||||
server.registerChannel('ai', <IAIChannel>{
|
||||
call(command: string, arg: any): TPromise<any> {
|
||||
switch (command) {
|
||||
case 'create': {
|
||||
let handle = idPool++;
|
||||
let {key, eventPrefix, data} = arg;
|
||||
adapter[handle] = new AIAdapter(eventPrefix, data, key);
|
||||
return TPromise.as(handle);
|
||||
}
|
||||
case 'log': {
|
||||
let {handle, eventName, data} = arg;
|
||||
adapter[handle].log(eventName, data);
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
case 'dispose': {
|
||||
let {handle} = arg;
|
||||
adapter[handle].dispose();
|
||||
delete adapter[handle];
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// It is important to dispose the AI adapter properly because
|
||||
// only then they flush remaining data.
|
||||
process.on('SIGTERM', function () {
|
||||
console.log('HERE');
|
||||
let promises: TPromise<any>[] = [];
|
||||
for (let handle in adapter) {
|
||||
let ai = adapter[handle];
|
||||
promises.push(ai.dispose());
|
||||
}
|
||||
TPromise.join(promises).then(_ => process.exit(0));
|
||||
});
|
16
src/vs/base/parts/ai/node/ai.ipc.ts
Normal file
16
src/vs/base/parts/ai/node/ai.ipc.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 {IChannel} from 'vs/base/parts/ipc/common/ipc';
|
||||
|
||||
export interface IAIChannel extends IChannel {
|
||||
call(command: 'create', data: { key: string; eventPrefix: string; data: { [k: string]: any }; }): TPromise<number>;
|
||||
call(command: 'log', data: { handle: number; eventName: string; data: { [k: string]: any }; }): TPromise<any>;
|
||||
call(command: 'dispose', data: { handle: number; }): TPromise<any>;
|
||||
call(command: string, arg: any): TPromise<any>;
|
||||
}
|
53
src/vs/base/parts/ai/node/ai.ts
Normal file
53
src/vs/base/parts/ai/node/ai.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 {IAIChannel} from './ai.ipc';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {connect} from 'vs/base/parts/ipc/node/ipc.net';
|
||||
|
||||
export interface IAIAdapter {
|
||||
log(eventName: string, data?: any): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export function createAIAdapter(key: string, eventPrefix: string, data: { [key: string]: any }): IAIAdapter {
|
||||
|
||||
let beforeReadyMessages: { type: string; args: any }[] = [];
|
||||
let handle: number = undefined;
|
||||
let channel: IAIChannel = {
|
||||
call(type: string, args: any): TPromise<any> {
|
||||
beforeReadyMessages.push({ type, args });
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
};
|
||||
|
||||
connect(process.env['VSCODE_SHARED_IPC_HOOK']).then(client => client.getChannel<IAIChannel>('ai')).then(actualChannel => {
|
||||
|
||||
return actualChannel.call('create', { key, eventPrefix, data }).then(actualHandle => {
|
||||
// channel has been created, store handle etc,
|
||||
// and flush all early messages
|
||||
handle = actualHandle;
|
||||
channel = actualChannel;
|
||||
for (let m of beforeReadyMessages) {
|
||||
let {type, args} = m;
|
||||
args.handle = handle;
|
||||
channel.call(type, args);
|
||||
}
|
||||
beforeReadyMessages.length = 0;
|
||||
});
|
||||
});
|
||||
|
||||
return <IAIAdapter>{
|
||||
log(eventName: string, data?: any) {
|
||||
channel.call('log', { handle, eventName, data });
|
||||
},
|
||||
dispose() {
|
||||
channel.call('dispose', { handle });
|
||||
}
|
||||
};
|
||||
|
||||
}
|
153
src/vs/base/parts/ai/node/aiAdapter.ts
Normal file
153
src/vs/base/parts/ai/node/aiAdapter.ts
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 appInsights from 'applicationinsights';
|
||||
import {isObject} from 'vs/base/common/types';
|
||||
import {safeStringify, mixin} from 'vs/base/common/objects';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
|
||||
namespace AI {
|
||||
|
||||
let _initialized = false;
|
||||
|
||||
function ensureAIEngineIsInitialized(): void {
|
||||
if (_initialized === false) {
|
||||
// we need to pass some fake key, otherwise AI throws an exception
|
||||
appInsights.setup('2588e01f-f6c9-4cd6-a348-143741f8d702')
|
||||
.setAutoCollectConsole(false)
|
||||
.setAutoCollectExceptions(false)
|
||||
.setAutoCollectPerformance(false)
|
||||
.setAutoCollectRequests(false);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function getClient(aiKey: string): typeof appInsights.client {
|
||||
|
||||
ensureAIEngineIsInitialized();
|
||||
|
||||
const client = appInsights.getClient(aiKey);
|
||||
client.channel.setOfflineMode(true);
|
||||
client.context.tags[client.context.keys.deviceMachineName] = ''; //prevent App Insights from reporting machine name
|
||||
if (aiKey.indexOf('AIF-') === 0) {
|
||||
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
interface Properties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Measurements {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
export class AIAdapter {
|
||||
|
||||
private _aiClient: typeof appInsights.client;
|
||||
|
||||
constructor(
|
||||
private _eventPrefix: string,
|
||||
private _defaultData: { [key: string]: any },
|
||||
aiKeyOrClientFactory: string | (() => typeof appInsights.client) // allow factory function for testing
|
||||
) {
|
||||
if (!this._defaultData) {
|
||||
this._defaultData = Object.create(null);
|
||||
}
|
||||
|
||||
if (typeof aiKeyOrClientFactory === 'string') {
|
||||
this._aiClient = AI.getClient(aiKeyOrClientFactory);
|
||||
} else if (typeof aiKeyOrClientFactory === 'function') {
|
||||
this._aiClient = aiKeyOrClientFactory();
|
||||
}
|
||||
}
|
||||
|
||||
private static _getData(data?: any): { properties: Properties, measurements: Measurements } {
|
||||
|
||||
const properties: Properties = Object.create(null);
|
||||
const measurements: Measurements = Object.create(null);
|
||||
|
||||
const flat = Object.create(null);
|
||||
AIAdapter._flaten(data, flat);
|
||||
|
||||
for (let prop in flat) {
|
||||
// enforce property names less than 150 char, take the last 150 char
|
||||
prop = prop.length > 150 ? prop.substr(prop.length - 149) : prop;
|
||||
var value = flat[prop];
|
||||
|
||||
if (typeof value === 'number') {
|
||||
measurements[prop] = value;
|
||||
|
||||
} else if (typeof value === 'boolean') {
|
||||
measurements[prop] = value ? 1 : 0;
|
||||
|
||||
} else if (typeof value === 'string') {
|
||||
//enforce property value to be less than 1024 char, take the first 1024 char
|
||||
properties[prop] = value.substring(0, 1023);
|
||||
|
||||
} else if (typeof value !== 'undefined' && value !== null) {
|
||||
properties[prop] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
properties,
|
||||
measurements
|
||||
};
|
||||
}
|
||||
|
||||
private static _flaten(obj: any, result: {[key: string]: any }, order: number = 0, prefix?: string): void {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(var item of Object.getOwnPropertyNames(obj)){
|
||||
const value = obj[item];
|
||||
const index = prefix ? prefix + item : item;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
result[index] = safeStringify(value);
|
||||
|
||||
} else if (value instanceof Date) {
|
||||
// TODO unsure why this is here and not in _getData
|
||||
result[index] = value.toISOString();
|
||||
|
||||
} else if (isObject(value)) {
|
||||
if (order < 2) {
|
||||
AIAdapter._flaten(value, result, order + 1, index + '.');
|
||||
} else {
|
||||
result[index] = safeStringify(value);
|
||||
}
|
||||
} else {
|
||||
result[index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public log(eventName: string, data?: any): void {
|
||||
if (!this._aiClient) {
|
||||
return;
|
||||
}
|
||||
data = mixin(data, this._defaultData);
|
||||
let {properties, measurements} = AIAdapter._getData(data);
|
||||
this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, properties, measurements);
|
||||
}
|
||||
|
||||
public dispose(): TPromise<any> {
|
||||
if (this._aiClient) {
|
||||
return new TPromise(resolve => {
|
||||
this._aiClient.sendPendingData(() => {
|
||||
// all data flushed
|
||||
this._aiClient = undefined;
|
||||
resolve(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Promise} from 'vs/base/common/winjs.base';
|
||||
import { TPromise, Promise} from 'vs/base/common/winjs.base';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { clone, assign } from 'vs/base/common/objects';
|
||||
import { Server as IPCServer, Client as IPCClient, IClient, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
|
@ -58,15 +58,13 @@ export class Client implements IClient, IDisposable {
|
|||
|
||||
private disposeDelayer: Delayer<void>;
|
||||
private activeRequests: Promise[];
|
||||
private child: ChildProcess;
|
||||
private _client: IPCClient;
|
||||
private channels: { [name: string]: IChannel };
|
||||
private _client: TPromise<[IPCClient, ChildProcess]>;
|
||||
private channels: { [name: string]: TPromise<IChannel> };
|
||||
|
||||
constructor(private modulePath: string, private options: IIPCOptions) {
|
||||
const timeout = options && options.timeout ? options.timeout : 60000;
|
||||
this.disposeDelayer = new Delayer<void>(timeout);
|
||||
this.activeRequests = [];
|
||||
this.child = null;
|
||||
this._client = null;
|
||||
this.channels = Object.create(null);
|
||||
}
|
||||
|
@ -79,8 +77,8 @@ export class Client implements IClient, IDisposable {
|
|||
protected request(channelName: string, name: string, arg: any): Promise {
|
||||
this.disposeDelayer.cancel();
|
||||
|
||||
const channel = this.channels[channelName] || (this.channels[channelName] = this.client.getChannel(channelName));
|
||||
const request: Promise = channel.call(name, arg);
|
||||
const channel = this.channels[channelName] || (this.channels[channelName] = this.client.then(value => value[0].getChannel(channelName)));
|
||||
const request: Promise = channel.then(channel => channel.call(name, arg));
|
||||
|
||||
// Progress doesn't propagate across 'then', we need to create a promise wrapper
|
||||
const result = new Promise((c, e, p) => {
|
||||
|
@ -98,72 +96,78 @@ export class Client implements IClient, IDisposable {
|
|||
return result;
|
||||
}
|
||||
|
||||
private get client(): IPCClient {
|
||||
private get client(): TPromise<[IPCClient, ChildProcess]> {
|
||||
if (!this._client) {
|
||||
const args = this.options && this.options.args ? this.options.args : [];
|
||||
let forkOpts:any = undefined;
|
||||
this._client = TPromise.timeout(0).then(() => { // since fork is expensive and we end up here often
|
||||
// during startup, we do a timeout(0) which is a setImmediate
|
||||
|
||||
if (this.options) {
|
||||
forkOpts = Object.create(null);
|
||||
const args = this.options && this.options.args ? this.options.args : [];
|
||||
let forkOpts:any = undefined;
|
||||
|
||||
if (this.options.env) {
|
||||
forkOpts.env = assign(clone(process.env), this.options.env);
|
||||
if (this.options) {
|
||||
forkOpts = Object.create(null);
|
||||
|
||||
if (this.options.env) {
|
||||
forkOpts.env = assign(clone(process.env), this.options.env);
|
||||
}
|
||||
|
||||
if (typeof this.options.debug === 'number') {
|
||||
forkOpts.execArgv = ['--nolazy', '--debug=' + this.options.debug];
|
||||
}
|
||||
|
||||
if (typeof this.options.debugBrk === 'number') {
|
||||
forkOpts.execArgv = ['--nolazy', '--debug-brk=' + this.options.debugBrk];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.options.debug === 'number') {
|
||||
forkOpts.execArgv = ['--nolazy', '--debug=' + this.options.debug];
|
||||
}
|
||||
const child = fork(this.modulePath, args, forkOpts);
|
||||
const client = new IPCClient({
|
||||
send: r => child && child.connected && child.send(r),
|
||||
onMessage: cb => {
|
||||
child.on('message', (msg) => {
|
||||
|
||||
if (typeof this.options.debugBrk === 'number') {
|
||||
forkOpts.execArgv = ['--nolazy', '--debug-brk=' + this.options.debugBrk];
|
||||
}
|
||||
}
|
||||
// Handle console logs specially
|
||||
if (msg && msg.type === '__$console') {
|
||||
let args = ['%c[IPC Library: ' + this.options.serverName + ']', 'color: darkgreen'];
|
||||
try {
|
||||
const parsed = JSON.parse(msg.arguments);
|
||||
args = args.concat(Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
|
||||
} catch (error) {
|
||||
args.push(msg.arguments);
|
||||
}
|
||||
|
||||
this.child = fork(this.modulePath, args, forkOpts);
|
||||
this._client = new IPCClient({
|
||||
send: r => this.child && this.child.connected && this.child.send(r),
|
||||
onMessage: cb => {
|
||||
this.child.on('message', (msg) => {
|
||||
|
||||
// Handle console logs specially
|
||||
if (msg && msg.type === '__$console') {
|
||||
let args = ['%c[IPC Library: ' + this.options.serverName + ']', 'color: darkgreen'];
|
||||
try {
|
||||
const parsed = JSON.parse(msg.arguments);
|
||||
args = args.concat(Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
|
||||
} catch (error) {
|
||||
args.push(msg.arguments);
|
||||
console[msg.severity].apply(console, args);
|
||||
}
|
||||
|
||||
console[msg.severity].apply(console, args);
|
||||
}
|
||||
// Anything else goes to the outside
|
||||
else {
|
||||
cb(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Anything else goes to the outside
|
||||
else {
|
||||
cb(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
const onExit = () => this.disposeClient();
|
||||
process.once('exit', onExit);
|
||||
|
||||
const onExit = () => this.disposeClient();
|
||||
process.once('exit', onExit);
|
||||
child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err));
|
||||
|
||||
this.child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err));
|
||||
child.on('exit', (code: any, signal: any) => {
|
||||
process.removeListener('exit', onExit);
|
||||
|
||||
this.child.on('exit', (code: any, signal: any) => {
|
||||
process.removeListener('exit', onExit);
|
||||
if (this.activeRequests) {
|
||||
this.activeRequests.forEach(req => req.cancel());
|
||||
this.activeRequests = [];
|
||||
}
|
||||
|
||||
if (this.activeRequests) {
|
||||
this.activeRequests.forEach(req => req.cancel());
|
||||
this.activeRequests = [];
|
||||
}
|
||||
if (code && signal !== 'SIGTERM') {
|
||||
console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code);
|
||||
this.disposeDelayer.cancel();
|
||||
this.disposeClient();
|
||||
}
|
||||
});
|
||||
|
||||
if (code && signal !== 'SIGTERM') {
|
||||
console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code);
|
||||
this.disposeDelayer.cancel();
|
||||
this.disposeClient();
|
||||
}
|
||||
return [client, child];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -172,10 +176,12 @@ export class Client implements IClient, IDisposable {
|
|||
|
||||
private disposeClient() {
|
||||
if (this._client) {
|
||||
this.child.kill();
|
||||
this.child = null;
|
||||
this._client = null;
|
||||
this._client.done(value => {
|
||||
let [, child] = value;
|
||||
child.kill();
|
||||
});
|
||||
this.channels = Object.create(null);
|
||||
this._client = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import DOM = require('vs/base/browser/dom');
|
|||
import {IActionProvider} from 'vs/base/parts/tree/browser/actionsRenderer';
|
||||
import {KeyCode} from 'vs/base/common/keyCodes';
|
||||
import {IDisposable,dispose} from 'vs/base/common/lifecycle';
|
||||
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
|
||||
export interface IQuickOpenCallbacks {
|
||||
onOk: () => void;
|
||||
|
@ -190,7 +191,7 @@ export class QuickOpenWidget implements IModelProvider {
|
|||
twistiePixels: 11,
|
||||
indentPixels: 0,
|
||||
alwaysFocused: true,
|
||||
verticalScrollMode: 'visible',
|
||||
verticalScrollMode: ScrollbarVisibility.Visible,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Quick Picker")
|
||||
});
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import Events = require('vs/base/common/eventEmitter');
|
|||
import Mouse = require('vs/base/browser/mouseEvent');
|
||||
import Keyboard = require('vs/base/browser/keyboardEvent');
|
||||
import { INavigator } from 'vs/base/common/iterator';
|
||||
import { ScrollbarVisibility } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
|
||||
export interface ITree extends Events.IEventEmitter {
|
||||
|
||||
|
@ -614,8 +615,7 @@ export interface ITreeConfiguration {
|
|||
export interface ITreeOptions {
|
||||
twistiePixels?: number;
|
||||
indentPixels?: number;
|
||||
horizontalScrollMode?: string;
|
||||
verticalScrollMode?: string;
|
||||
verticalScrollMode?: ScrollbarVisibility;
|
||||
alwaysFocused?: boolean;
|
||||
autoExpandSingleChildren?: boolean;
|
||||
bare?:boolean;
|
||||
|
|
|
@ -17,7 +17,8 @@ import Keyboard = require('vs/base/browser/keyboardEvent');
|
|||
import Model = require('vs/base/parts/tree/browser/treeModel');
|
||||
import dnd = require('./treeDnd');
|
||||
import { ArrayIterator, MappedIterator } from 'vs/base/common/iterator';
|
||||
import ScrollableElementImpl = require('vs/base/browser/ui/scrollbar/scrollableElement');
|
||||
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
|
||||
import { HeightMap } from 'vs/base/parts/tree/browser/treeViewModel';
|
||||
import _ = require('vs/base/parts/tree/browser/tree');
|
||||
import { IViewItem } from 'vs/base/parts/tree/browser/treeViewModel';
|
||||
|
@ -407,7 +408,7 @@ export class TreeView extends HeightMap {
|
|||
private domNode: HTMLElement;
|
||||
private wrapper: HTMLElement;
|
||||
private rowsContainer: HTMLElement;
|
||||
private scrollableElement: ScrollableElementImpl.ScrollableElement;
|
||||
private scrollableElement: ScrollableElement;
|
||||
private wrapperGesture: Touch.Gesture;
|
||||
private msGesture: MSGesture;
|
||||
private lastPointerType:string;
|
||||
|
@ -486,10 +487,10 @@ export class TreeView extends HeightMap {
|
|||
|
||||
this.wrapper = document.createElement('div');
|
||||
this.wrapper.className = 'monaco-tree-wrapper';
|
||||
this.scrollableElement = new ScrollableElementImpl.ScrollableElement(this.wrapper, {
|
||||
forbidTranslate3dUse: true,
|
||||
horizontal: 'hidden',
|
||||
vertical: context.options.verticalScrollMode || 'auto',
|
||||
this.scrollableElement = new ScrollableElement(this.wrapper, {
|
||||
canUseTranslate3d: false,
|
||||
horizontal: ScrollbarVisibility.Hidden,
|
||||
vertical: (typeof context.options.verticalScrollMode !== 'undefined' ? context.options.verticalScrollMode : ScrollbarVisibility.Auto),
|
||||
useShadows: context.options.useShadows,
|
||||
saveLastScrollTimeOnClassName: 'monaco-tree-row'
|
||||
});
|
||||
|
|
|
@ -19,35 +19,29 @@ suite('Browsers', () => {
|
|||
var isChrome = browser.isChrome;
|
||||
var isSafari = browser.isSafari;
|
||||
|
||||
var canPushState = browser.canPushState();
|
||||
var hasCSSAnimations = browser.hasCSSAnimationSupport();
|
||||
|
||||
var browserCount = 0;
|
||||
if (isOpera) {
|
||||
browserCount++;
|
||||
assert(canPushState);
|
||||
}
|
||||
if (isIE11orEarlier) {
|
||||
browserCount++;
|
||||
}
|
||||
if (isFirefox) {
|
||||
browserCount++;
|
||||
assert(canPushState);
|
||||
assert(hasCSSAnimations);
|
||||
}
|
||||
if (isWebKit) {
|
||||
browserCount++;
|
||||
assert(canPushState);
|
||||
assert(hasCSSAnimations);
|
||||
}
|
||||
if (isChrome) {
|
||||
browserCount++;
|
||||
assert(canPushState);
|
||||
assert(hasCSSAnimations);
|
||||
}
|
||||
if (isSafari) {
|
||||
browserCount++;
|
||||
assert(canPushState);
|
||||
assert(hasCSSAnimations);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import BrowserService = require('vs/base/browser/browserService');
|
||||
import { MockBrowserServiceData } from 'vs/base/test/browser/mockBrowserService';
|
||||
|
||||
suite('BrowserService', () => {
|
||||
test('Mocking of Window', () => {
|
||||
try {
|
||||
var service = BrowserService.getService();
|
||||
service.mock(new MockBrowserServiceData());
|
||||
var w = <any> service.window;
|
||||
w.testValue = 42;
|
||||
service.restore();
|
||||
w = <any> service.window;
|
||||
assert.strictEqual(w.testValue, undefined);
|
||||
}
|
||||
finally {
|
||||
if(service) {
|
||||
service.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MockDocument, MockWindow, MockElement } from 'vs/base/test/browser/mockDom';
|
||||
import browserService = require('vs/base/browser/browserService');
|
||||
|
||||
function mockedIsHTMLElement(o:any): boolean {
|
||||
if (typeof HTMLElement === 'object') {
|
||||
return o instanceof HTMLElement || o instanceof MockElement;
|
||||
}
|
||||
return o && typeof o === 'object' && o.nodeType === 1 && typeof o.nodeName === 'string';
|
||||
}
|
||||
|
||||
export class MockBrowserServiceData implements browserService.IBrowserServiceData {
|
||||
|
||||
public document:Document;
|
||||
public window:Window;
|
||||
public isHTMLElement: (o:any)=>boolean;
|
||||
|
||||
constructor() {
|
||||
this.document = <any> new MockDocument();
|
||||
this.window = <any> new MockWindow();
|
||||
this.isHTMLElement = mockedIsHTMLElement;
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import MockDom = require('vs/base/test/browser/mockDom');
|
||||
|
||||
suite('MockDom', () => {
|
||||
test('Create Element', () => {
|
||||
var doc = new MockDom.MockDocument();
|
||||
var div = doc.createElement('div');
|
||||
div.textContent = 'test';
|
||||
assert.strictEqual(div.textContent, 'test');
|
||||
assert.strictEqual(div.tagName, 'div');
|
||||
});
|
||||
|
||||
test('Events', () => {
|
||||
var doc = new MockDom.MockDocument();
|
||||
var div = doc.createElement('div');
|
||||
var fakeEvent = <Event>{ type: 'test'};
|
||||
var count = 0;
|
||||
var callback = function(event:any) {
|
||||
assert.strictEqual(event, fakeEvent);
|
||||
count++;
|
||||
};
|
||||
div.addEventListener('test', callback);
|
||||
div.dispatchEvent(fakeEvent);
|
||||
assert.strictEqual(count, 1);
|
||||
div.removeEventListener('test', callback);
|
||||
div.dispatchEvent(fakeEvent);
|
||||
assert.strictEqual(count, 1);
|
||||
});
|
||||
|
||||
test('Create elements via innerHTML', () => {
|
||||
var doc = new MockDom.MockDocument();
|
||||
var div = doc.createElement('div');
|
||||
div.innerHTML = '<div id="testId"><span class="testClass">test</span></div>';
|
||||
assert.strictEqual(div.children.length, 1);
|
||||
assert.strictEqual(div.childNodes[0], div.children[0]);
|
||||
assert.strictEqual(div.children[0], div.firstElementChild);
|
||||
assert.strictEqual(div.firstElementChild, div.firstChild);
|
||||
var child = <HTMLElement> div.children[0];
|
||||
|
||||
assert.strictEqual(child.id, 'testId');
|
||||
assert.strictEqual(child.getAttribute('id'), 'testId');
|
||||
assert.strictEqual(child.childElementCount, 1);
|
||||
|
||||
var grandchild = <HTMLElement> child.firstElementChild;
|
||||
assert.strictEqual(grandchild.tagName, 'span');
|
||||
assert.strictEqual(grandchild.className, 'testClass');
|
||||
|
||||
assert.strictEqual(grandchild.textContent, 'test');
|
||||
});
|
||||
|
||||
test('Convert elements to innerHTML', () => {
|
||||
var doc = new MockDom.MockDocument();
|
||||
var div = doc.createElement('div');
|
||||
|
||||
var child = doc.createElement('div');
|
||||
child.id = 'testId';
|
||||
|
||||
var grandchild = doc.createElement('span');
|
||||
grandchild.className = 'testClass';
|
||||
grandchild.textContent = 'test';
|
||||
child.appendChild(grandchild);
|
||||
div.appendChild(child);
|
||||
|
||||
assert.strictEqual(div.innerHTML, '<div id="testId"><span class="testClass">test</span></div>');
|
||||
assert.strictEqual(div.outerHTML, '<div><div id="testId"><span class="testClass">test</span></div></div>');
|
||||
});
|
||||
});
|
|
@ -1,914 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
interface IEventMap {
|
||||
[name: string]: EventListener[];
|
||||
}
|
||||
|
||||
export class MockEventTarget implements EventTarget {
|
||||
|
||||
private eventMap:IEventMap;
|
||||
|
||||
constructor() {
|
||||
this.eventMap = {};
|
||||
}
|
||||
|
||||
removeEventListener(type: string, listener: EventListener, useCapture?: boolean): void {
|
||||
if(type in this.eventMap) {
|
||||
var a = this.eventMap[type];
|
||||
a.splice(a.indexOf(listener), 1);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void {
|
||||
if(type in this.eventMap) {
|
||||
this.eventMap[type].push(listener);
|
||||
}
|
||||
else {
|
||||
this.eventMap[type] = [listener];
|
||||
}
|
||||
}
|
||||
|
||||
dispatchEvent(evt: Event): boolean {
|
||||
var listeners = this.eventMap[evt.type];
|
||||
if (listeners) {
|
||||
listeners.forEach((listener) => {
|
||||
listener(evt);
|
||||
});
|
||||
}
|
||||
return evt.defaultPrevented;
|
||||
}
|
||||
}
|
||||
|
||||
export class MockNode extends MockEventTarget implements Node {
|
||||
// Added to make compiler happy. No real mocking
|
||||
classList: DOMTokenList;
|
||||
baseURI: string;
|
||||
|
||||
parentElement: HTMLElement;
|
||||
nodeType: number;
|
||||
previousSibling: Node;
|
||||
localName: string;
|
||||
namespaceURI: string;
|
||||
parentNode: Node;
|
||||
nextSibling: Node;
|
||||
nodeValue: string;
|
||||
|
||||
public _childNodes: Node[];
|
||||
nodeName: string;
|
||||
ownerDocument: Document;
|
||||
_attributes: Attr[];
|
||||
|
||||
prefix: string;
|
||||
|
||||
|
||||
constructor(name:string) {
|
||||
super();
|
||||
this.nodeName = name;
|
||||
this._childNodes = [];
|
||||
this._attributes = [];
|
||||
}
|
||||
|
||||
public get attributes(): NamedNodeMap {
|
||||
return <any> this._attributes;
|
||||
}
|
||||
|
||||
public get lastChild(): Node {
|
||||
return this._childNodes[this._childNodes.length - 1];
|
||||
}
|
||||
public get firstChild(): Node {
|
||||
return this._childNodes[0];
|
||||
}
|
||||
|
||||
public get childNodes(): NodeList {
|
||||
var a = <any>this._childNodes;
|
||||
if(!a.item) {
|
||||
a.item = (function(index:number) {
|
||||
return this[index];
|
||||
}).bind(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
public get textContent(): string {
|
||||
|
||||
return this._childNodes.filter((node) => {
|
||||
return node.nodeType === this.TEXT_NODE;
|
||||
}).map((node) => {
|
||||
return (<Text>node).wholeText;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
public set textContent(value:string) {
|
||||
this._childNodes = [];
|
||||
this.appendChild(this.ownerDocument.createTextNode(value));
|
||||
}
|
||||
|
||||
public removeChild(oldChild: Node): Node {
|
||||
var i = this._childNodes.indexOf(oldChild);
|
||||
if(i >= 0) {
|
||||
var removed = this._childNodes.splice(i, 1);
|
||||
return removed[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public contains(node: Node): boolean {
|
||||
return this._childNodes.indexOf(node) !== -1;
|
||||
}
|
||||
|
||||
appendChild(newChild: Node): Node {
|
||||
this._childNodes.push(newChild);
|
||||
return newChild;
|
||||
}
|
||||
|
||||
isSupported(feature: string, version: string): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
isEqualNode(arg: Node): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
lookupPrefix(namespaceURI: string): string {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
isDefaultNamespace(namespaceURI: string): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
compareDocumentPosition(other: Node): number {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
normalize(): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
isSameNode(other: Node): boolean {
|
||||
return this === other;
|
||||
}
|
||||
hasAttributes(): boolean {
|
||||
return this.attributes.length > 0;
|
||||
}
|
||||
lookupNamespaceURI(prefix: string): string {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
cloneNode(deep?: boolean): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
hasChildNodes(): boolean {
|
||||
return this.childNodes.length > 0;
|
||||
}
|
||||
replaceChild(newChild: Node, oldChild: Node): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
insertBefore(newChild: Node, refChild?: Node): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
ENTITY_REFERENCE_NODE: number = Node.ENTITY_REFERENCE_NODE;
|
||||
ATTRIBUTE_NODE: number = Node.ATTRIBUTE_NODE;
|
||||
DOCUMENT_FRAGMENT_NODE: number = Node.DOCUMENT_FRAGMENT_NODE;
|
||||
TEXT_NODE: number = Node.TEXT_NODE;
|
||||
ELEMENT_NODE: number = Node.ELEMENT_NODE;
|
||||
COMMENT_NODE: number = Node.COMMENT_NODE;
|
||||
DOCUMENT_POSITION_DISCONNECTED: number = Node.DOCUMENT_POSITION_DISCONNECTED;
|
||||
DOCUMENT_POSITION_CONTAINED_BY: number = Node.DOCUMENT_POSITION_CONTAINED_BY;
|
||||
DOCUMENT_POSITION_CONTAINS: number = Node.DOCUMENT_POSITION_CONTAINS;
|
||||
DOCUMENT_TYPE_NODE: number = Node.DOCUMENT_TYPE_NODE;
|
||||
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
|
||||
DOCUMENT_NODE: number = Node.DOCUMENT_NODE;
|
||||
ENTITY_NODE: number = Node.ENTITY_NODE;
|
||||
PROCESSING_INSTRUCTION_NODE: number = Node.PROCESSING_INSTRUCTION_NODE;
|
||||
CDATA_SECTION_NODE: number = Node.CDATA_SECTION_NODE;
|
||||
NOTATION_NODE: number = Node.NOTATION_NODE;
|
||||
DOCUMENT_POSITION_FOLLOWING: number = Node.DOCUMENT_POSITION_FOLLOWING;
|
||||
DOCUMENT_POSITION_PRECEDING: number = Node.DOCUMENT_POSITION_PRECEDING;
|
||||
}
|
||||
|
||||
export class MockAttribute extends MockNode implements Attr {
|
||||
|
||||
ownerElement: Element;
|
||||
value: string;
|
||||
name: string;
|
||||
// MSAttrExtensions
|
||||
expando: boolean;
|
||||
|
||||
constructor(name:string) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
this.expando = false;
|
||||
}
|
||||
|
||||
public get specified(): boolean {
|
||||
return !!this.value;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ClientRect {
|
||||
left: number;
|
||||
width: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export class MockElement extends MockNode implements Element {
|
||||
// Added to make compiler happy. No real mocking
|
||||
classList: DOMTokenList;
|
||||
id: string;
|
||||
className: string;
|
||||
|
||||
scrollTop: number;
|
||||
clientLeft: number;
|
||||
scrollLeft: number;
|
||||
tagName: string;
|
||||
clientWidth: number;
|
||||
scrollWidth: number;
|
||||
clientHeight: number;
|
||||
clientTop: number;
|
||||
scrollHeight: number;
|
||||
|
||||
constructor(tagName: string) {
|
||||
super(tagName);
|
||||
this.tagName = tagName;
|
||||
}
|
||||
|
||||
getAttribute(name?: string): string {
|
||||
var filter = this._attributes.filter((attr) => {
|
||||
return attr.name === name;
|
||||
});
|
||||
|
||||
return filter.length ? filter[0].value : '';
|
||||
}
|
||||
|
||||
get innerHTML(): string {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
set innerHTML(value: string) {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
getElementsByTagNameNS(namespaceURI: string, localName: string): NodeListOf<Element> {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getElementsByClassName(classNames: string): NodeListOf<Element> {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
hasAttributeNS(namespaceURI: string, localName: string): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getBoundingClientRect(): ClientRect {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getAttributeNS(namespaceURI: string, localName: string): string {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getAttributeNodeNS(namespaceURI: string, localName: string): Attr {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
setAttributeNodeNS(newAttr: Attr): Attr {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
hasAttribute(name: string): boolean {
|
||||
var filter = this._attributes.filter((attr) => {
|
||||
return attr.name === name;
|
||||
});
|
||||
|
||||
return filter.length > 0;
|
||||
}
|
||||
removeAttribute(name?: string): void {
|
||||
this._attributes = this._attributes.filter((attr) => {
|
||||
return attr.name !== name;
|
||||
});
|
||||
}
|
||||
setAttributeNS(namespaceURI: string, qualifiedName: string, value: string): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getAttributeNode(name: string): Attr {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getElementsByTagName(name: string): NodeListOf<Element> {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
setAttributeNode(newAttr: Attr): Attr {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
getClientRects(): ClientRectList {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
removeAttributeNode(oldAttr: Attr): Attr {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
setAttribute(name?: string, value?: string): void {
|
||||
if(this.hasAttribute(name)) {
|
||||
this.removeAttribute(name);
|
||||
}
|
||||
var attr = this.ownerDocument.createAttribute(name);
|
||||
attr.ownerElement = this;
|
||||
attr.value = value;
|
||||
this._attributes.push(attr);
|
||||
}
|
||||
removeAttributeNS(namespaceURI: string, localName: string): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
matches(selector: string): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
// interface NodeSelector
|
||||
querySelectorAll(selectors: string): NodeListOf<Element> {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
querySelector(selectors: string): Element {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
|
||||
// interface ElementTraversal
|
||||
public get childElementCount(): number {
|
||||
return this._childNodes.filter((node) => {
|
||||
return node.nodeType === this.ELEMENT_NODE;
|
||||
}).length;
|
||||
}
|
||||
previousElementSibling: Element;
|
||||
public get lastElementChild(): Element {
|
||||
var a = this._childNodes.filter((node) => {
|
||||
return node.nodeType === this.ELEMENT_NODE;
|
||||
});
|
||||
return <any>a[a.length - 1];
|
||||
}
|
||||
nextElementSibling: Element;
|
||||
public get firstElementChild(): Element {
|
||||
var a = this._childNodes.filter((node) => {
|
||||
return node.nodeType === this.ELEMENT_NODE;
|
||||
});
|
||||
return <any>a[0];
|
||||
}
|
||||
|
||||
// interface MSElementExtensions
|
||||
msMatchesSelector(selectors: string): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
fireEvent(eventName: string, eventObj?: any): boolean {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
// other interface msElementExtensions
|
||||
msZoomTo(args: any) {}
|
||||
msRequestFullscreen() {}
|
||||
msGetUntransformedBounds():ClientRect {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
msRegionOverflow: string;
|
||||
|
||||
requestFullscreen(): void { throw new Error('Not implemented!'); }
|
||||
requestPointerLock(): void { throw new Error('Not implemented!'); }
|
||||
|
||||
webkitMatchesSelector(selectors: string): boolean { throw new Error('Not implemented!'); }
|
||||
webkitRequestFullScreen(): void { throw new Error('Not implemented!'); }
|
||||
webkitRequestFullscreen(): void { throw new Error('Not implemented!'); }
|
||||
|
||||
remove(): void { throw new Error('Not implemented!'); }
|
||||
|
||||
onariarequest: (ev: any) => any;
|
||||
oncommand: (ev: any) => any;
|
||||
// Pointer events new in IE 11
|
||||
onlostpointercapture: (ev: any) => any;
|
||||
ongotpointercapture: (ev: any) => any;
|
||||
setPointerCapture: (ev: any) => any;
|
||||
releasePointerCapture: (ev: any) => any;
|
||||
onpointerenter: (ev: any) => any;
|
||||
onpointerout: (ev: any) => any;
|
||||
onpointerdown: (ev: any) => any;
|
||||
onpointerup: (ev: any) => any;
|
||||
onpointercancel: (ev: any) => any;
|
||||
onpointerover: (ev: any) => any;
|
||||
onpointermove: (ev: any) => any;
|
||||
onpointerleave: (ev: any) => any;
|
||||
|
||||
onwheel: (ev: any) => any;
|
||||
|
||||
ontouchcancel: (ev: any) => any;
|
||||
ontouchend: (ev: any) => any;
|
||||
ontouchmove: (ev: any) => any;
|
||||
ontouchstart: (ev: any) => any;
|
||||
|
||||
onmspointerenter: (ev:any) => any;
|
||||
onmspointerleave: (ev:any) => any;
|
||||
onmspointerdown: (ev: any) => any;
|
||||
onmsgotpointercapture: (ev: any) => any;
|
||||
onmsgesturedoubletap: (ev: any) => any;
|
||||
onmspointerhover: (ev: any) => any;
|
||||
onmsgesturehold: (ev: any) => any;
|
||||
onmspointermove: (ev: any) => any;
|
||||
onmsgesturechange: (ev: any) => any;
|
||||
onmsgesturestart: (ev: any) => any;
|
||||
onmspointercancel: (ev: any) => any;
|
||||
onmsgestureend: (ev: any) => any;
|
||||
onmsgesturetap: (ev: any) => any;
|
||||
onmspointerout: (ev: any) => any;
|
||||
onmsinertiastart: (ev: any) => any;
|
||||
onmslostpointercapture: (ev: any) => any;
|
||||
onmspointerover: (ev: any) => any;
|
||||
msContentZoomFactor: number;
|
||||
onmspointerup: (ev: any) => any;
|
||||
|
||||
onwebkitfullscreenchange: (ev: Event) => any;
|
||||
onwebkitfullscreenerror: (ev: Event) => any;
|
||||
|
||||
msGetRegionContent(): MSRangeCollection {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
msReleasePointerCapture(pointerId: number): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
msSetPointerCapture(pointerId: number): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockCharacterData extends MockNode implements CharacterData {
|
||||
length: number;
|
||||
data: string;
|
||||
|
||||
constructor(text:string) {
|
||||
super(text);
|
||||
this.nodeType = this.TEXT_NODE;
|
||||
this.length = text.length;
|
||||
this.data = text;
|
||||
}
|
||||
|
||||
deleteData(offset: number, count: number): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
replaceData(offset: number, count: number, arg: string): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
appendData(arg: string): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
insertData(offset: number, arg: string): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
substringData(offset: number, count: number): string {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
remove(): void { throw new Error('Not implemented!'); }
|
||||
}
|
||||
|
||||
export class MockText extends MockCharacterData implements Text {
|
||||
wholeText: string;
|
||||
|
||||
constructor(text:string) {
|
||||
super(text);
|
||||
this.wholeText = text;
|
||||
}
|
||||
|
||||
splitText(offset: number): Text {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
replaceWholeText(content: string): Text {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
swapNode(otherNode: Node): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
removeNode(deep?: boolean): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
replaceNode(replacement: Node): Node {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockHTMLElement extends MockElement /* implements HTMLElement */ {
|
||||
|
||||
public style: CSSStyleDeclaration;
|
||||
|
||||
constructor(tagName: string) {
|
||||
super(tagName);
|
||||
this.style = <any> {};
|
||||
this.nodeType = this.ELEMENT_NODE;
|
||||
}
|
||||
|
||||
public get className(): string {
|
||||
return this.getAttribute('class');
|
||||
}
|
||||
|
||||
public set className(value:string) {
|
||||
this.setAttribute('class', value);
|
||||
}
|
||||
|
||||
public get id():string {
|
||||
return this.getAttribute('id');
|
||||
}
|
||||
|
||||
public set id(value:string) {
|
||||
this.setAttribute('id', value);
|
||||
}
|
||||
|
||||
public get children(): HTMLCollection {
|
||||
var a = <any> this._childNodes.filter((node) => {
|
||||
return node.nodeType === this.ELEMENT_NODE;
|
||||
});
|
||||
if(!a.item) {
|
||||
a.item = (function(index:number) {
|
||||
return this[index];
|
||||
}).bind(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
get outerHTML(): string {
|
||||
var stringer = new DOMStringer(this);
|
||||
return stringer.toString(true);
|
||||
}
|
||||
|
||||
get innerHTML(): string {
|
||||
var stringer = new DOMStringer(this);
|
||||
return stringer.toString();
|
||||
}
|
||||
|
||||
set innerHTML(value:string) {
|
||||
var parser = new DOMParser(this.ownerDocument);
|
||||
var nodes = parser.parse(value);
|
||||
nodes.forEach((node) => {
|
||||
this.appendChild(node);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MockDocument extends MockEventTarget /*implements Document*/ {
|
||||
public createElement(tagName: string): HTMLElement {
|
||||
var e = new MockHTMLElement(tagName);
|
||||
e.ownerDocument = <any> this;
|
||||
return <any> e;
|
||||
}
|
||||
public createTextNode(data: string): Text {
|
||||
var n = new MockText(data);
|
||||
n.ownerDocument = <any> this;
|
||||
return n;
|
||||
}
|
||||
public createAttribute(name: string): Attr {
|
||||
var a = new MockAttribute(name);
|
||||
a.ownerDocument = <any> this;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
export class MockWindow /*implements Window*/ {
|
||||
|
||||
}
|
||||
|
||||
interface IParserState {
|
||||
name: string;
|
||||
consumeCharacter(stream:StringStream):IParserState;
|
||||
onTransition(parser:DOMParser, nextState:string):void;
|
||||
|
||||
}
|
||||
|
||||
class ErrorState implements IParserState {
|
||||
public name: string;
|
||||
public message:string;
|
||||
|
||||
constructor(message:string) {
|
||||
this.name = 'error';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
consumeCharacter(stream:StringStream):IParserState {
|
||||
return this;
|
||||
}
|
||||
|
||||
onTransition(parser:DOMParser, nextState:string):void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TextParser implements IParserState {
|
||||
|
||||
public name: string;
|
||||
private textContent: string;
|
||||
|
||||
constructor() {
|
||||
this.name = 'text';
|
||||
this.textContent = '';
|
||||
}
|
||||
|
||||
consumeCharacter(stream:StringStream):IParserState {
|
||||
var char = stream.next();
|
||||
switch(char) {
|
||||
case '<':
|
||||
return new TagParser();
|
||||
case '>':
|
||||
return new ErrorState('Unexpected >');
|
||||
default:
|
||||
this.textContent += char;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
onTransition(parser:DOMParser, nextState:string):void {
|
||||
if(this.textContent) {
|
||||
var node = parser.document.createTextNode(this.textContent);
|
||||
if(parser.currentNode) {
|
||||
parser.currentNode.appendChild(node);
|
||||
} else {
|
||||
parser.root.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IMap {
|
||||
[name: string]:string;
|
||||
}
|
||||
|
||||
class TagParser implements IParserState {
|
||||
|
||||
public name: string;
|
||||
private isClosing:boolean;
|
||||
private tagName:string;
|
||||
public attributes: IMap;
|
||||
|
||||
constructor() {
|
||||
this.name = 'tag';
|
||||
this.tagName = '';
|
||||
this.isClosing = false;
|
||||
this.attributes = {};
|
||||
}
|
||||
|
||||
consumeCharacter(stream:StringStream):IParserState {
|
||||
var char = stream.next();
|
||||
switch(char) {
|
||||
case '/':
|
||||
this.isClosing = true;
|
||||
return this;
|
||||
case '>':
|
||||
if(this.tagName) {
|
||||
return new TextParser();
|
||||
}
|
||||
else {
|
||||
return new ErrorState('No tag name specified');
|
||||
}
|
||||
case ' ':
|
||||
if(this.tagName) {
|
||||
if(this.isClosing) {
|
||||
return new ErrorState('Closing tags cannot have attributes');
|
||||
}
|
||||
return new AttributeParser(this);
|
||||
} else {
|
||||
return new ErrorState('Tag name must be first.');
|
||||
}
|
||||
default:
|
||||
this.tagName += char;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
onTransition(parser:DOMParser, nextState:string):void {
|
||||
if(this.tagName && nextState !== 'attribute') {
|
||||
if(this.isClosing) {
|
||||
if(parser.openElements[parser.openElements.length - 1].tagName !== this.tagName) {
|
||||
throw new Error('Mismatched closing tag:' + this.tagName);
|
||||
} else {
|
||||
parser.openElements.pop();
|
||||
if(parser.openElements.length) {
|
||||
parser.currentNode = parser.openElements[parser.openElements.length - 1];
|
||||
} else {
|
||||
parser.currentNode = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var node = parser.document.createElement(this.tagName);
|
||||
Object.keys(this.attributes).forEach((key) => {
|
||||
node.setAttribute(key, this.attributes[key]);
|
||||
});
|
||||
if(parser.currentNode) {
|
||||
parser.currentNode.appendChild(node);
|
||||
} else {
|
||||
parser.root.push(node);
|
||||
}
|
||||
parser.openElements.push(node);
|
||||
parser.currentNode = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AttributeParser implements IParserState {
|
||||
public name: string;
|
||||
private tag: TagParser;
|
||||
private attributeName:string;
|
||||
public attributeValue:string;
|
||||
private inValue:boolean;
|
||||
|
||||
constructor(tag: TagParser) {
|
||||
this.name = 'attribute';
|
||||
this.tag = tag;
|
||||
this.inValue = false;
|
||||
this.attributeName = '';
|
||||
}
|
||||
|
||||
consumeCharacter(stream:StringStream):IParserState {
|
||||
var char = stream.next();
|
||||
switch(char) {
|
||||
case ' ':
|
||||
if(this.inValue) {
|
||||
return this.tag;
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
case '=':
|
||||
this.inValue = true;
|
||||
return new AttributeValueParser(this);
|
||||
case '>':
|
||||
stream.back();
|
||||
return this.tag;
|
||||
default:
|
||||
if(this.inValue === false) {
|
||||
this.attributeName += char;
|
||||
}
|
||||
return this;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onTransition(parser:DOMParser, nextState:string):void {
|
||||
if(nextState !== 'attributeValue') {
|
||||
this.tag.attributes[this.attributeName] = this.attributeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AttributeValueParser implements IParserState {
|
||||
public name: string;
|
||||
private attribute: AttributeParser;
|
||||
private value:string;
|
||||
private quote:boolean;
|
||||
|
||||
constructor(attribute:AttributeParser) {
|
||||
this.name = 'attributeValue';
|
||||
this.attribute = attribute;
|
||||
this.value = '';
|
||||
this.quote = false;
|
||||
}
|
||||
|
||||
consumeCharacter(stream:StringStream):IParserState {
|
||||
var char = stream.next();
|
||||
switch(char) {
|
||||
case '"':
|
||||
if(this.quote === false) {
|
||||
this.quote = true;
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
return this.attribute;
|
||||
}
|
||||
default:
|
||||
if(this.quote === false) {
|
||||
return new ErrorState('Expected " character');
|
||||
} else {
|
||||
this.value += char;
|
||||
}
|
||||
return this;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onTransition(parser:DOMParser, nextState:string):void {
|
||||
this.attribute.attributeValue = this.value;
|
||||
}
|
||||
}
|
||||
|
||||
class StringStream {
|
||||
private text: string;
|
||||
private index = 0;
|
||||
|
||||
constructor(text:string) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public more():boolean {
|
||||
return this.index < this.text.length;
|
||||
}
|
||||
|
||||
public next():string {
|
||||
|
||||
if(this.index >= this.text.length) {
|
||||
throw new Error('Past end of string!');
|
||||
}
|
||||
|
||||
return this.text[this.index++];
|
||||
}
|
||||
|
||||
public back() {
|
||||
this.index--;
|
||||
}
|
||||
}
|
||||
|
||||
class DOMParser {
|
||||
public activeState: IParserState;
|
||||
public currentNode: Node;
|
||||
public openElements: Element[];
|
||||
public root:Node[];
|
||||
public document:Document;
|
||||
|
||||
constructor(document:Document) {
|
||||
this.document = document;
|
||||
this.root = [];
|
||||
this.openElements = [];
|
||||
this.currentNode = null;
|
||||
this.activeState = new TextParser();
|
||||
}
|
||||
|
||||
public parse(text:string):Node[] {
|
||||
|
||||
var stream = new StringStream(text);
|
||||
while(stream.more()) {
|
||||
var nextState = this.activeState.consumeCharacter(stream);
|
||||
if(nextState !== this.activeState) {
|
||||
this.activeState.onTransition(this, nextState.name);
|
||||
this.activeState = nextState;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.activeState.name === 'error') {
|
||||
throw new Error((<ErrorState> this.activeState).message);
|
||||
}
|
||||
if(this.openElements.length !== 0) {
|
||||
throw new Error('Elements not closed: ' + this.openElements.map((element) => {
|
||||
return element.tagName;
|
||||
}).join());
|
||||
}
|
||||
return this.root;
|
||||
}
|
||||
}
|
||||
|
||||
class DOMStringer {
|
||||
|
||||
private root:Node;
|
||||
|
||||
constructor(root:Node) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private print(node:Node):string {
|
||||
var result = '';
|
||||
switch(node.nodeType) {
|
||||
case node.ELEMENT_NODE:
|
||||
result += this.printElement(<any>node);
|
||||
break;
|
||||
case node.TEXT_NODE:
|
||||
result += this.printText(<any>node);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private printChildren(node:Node):string {
|
||||
var result = '';
|
||||
if(node.hasChildNodes()) {
|
||||
for(var i = 0; i < node.childNodes.length; i++) {
|
||||
result += this.print(node.childNodes.item(i));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private printElement(element:Element):string {
|
||||
var result = ['<'];
|
||||
result.push(element.tagName);
|
||||
if(element.hasAttributes()) {
|
||||
var attributes:Attr[] = <any>element.attributes;
|
||||
result.push(attributes.reduce((prev:string, current:Attr) => {
|
||||
var attr = [prev, current.name];
|
||||
if(current.value) {
|
||||
attr.push('="', current.value, '"');
|
||||
}
|
||||
return attr.join('');
|
||||
}, ' '));
|
||||
}
|
||||
result.push('>');
|
||||
result.push(this.printChildren(element));
|
||||
result.push('</');
|
||||
result.push(element.tagName);
|
||||
result.push('>');
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
private printText(text:Text):string {
|
||||
return text.wholeText;
|
||||
}
|
||||
|
||||
public toString(includeRoot?:boolean):string {
|
||||
if(includeRoot) {
|
||||
return this.print(this.root);
|
||||
} else {
|
||||
return this.printChildren(this.root);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@
|
|||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { SyntaxKind, createScanner, parse } from 'vs/base/common/json';
|
||||
import { SyntaxKind, createScanner, parse, getLocation, Node, ParseError, parseTree, ParseErrorCode,
|
||||
getParseErrorMessage, ParseOptions, Segment, findNodeAtLocation, getNodeValue } from 'vs/base/common/json';
|
||||
|
||||
function assertKinds(text:string, ...kinds:SyntaxKind[]):void {
|
||||
var _json = createScanner(text);
|
||||
|
@ -17,24 +18,60 @@ function assertKinds(text:string, ...kinds:SyntaxKind[]):void {
|
|||
}
|
||||
|
||||
|
||||
function assertValidParse(input:string, expected:any) : void {
|
||||
var errors : string[] = [];
|
||||
var actual = parse(input, errors);
|
||||
function assertValidParse(input:string, expected:any, options?: ParseOptions) : void {
|
||||
var errors : {error: ParseErrorCode}[] = [];
|
||||
var actual = parse(input, errors, options);
|
||||
|
||||
if (errors.length !== 0) {
|
||||
assert(false, errors[0]);
|
||||
assert(false, getParseErrorMessage(errors[0].error));
|
||||
}
|
||||
assert.deepEqual(actual, expected);
|
||||
}
|
||||
|
||||
function assertInvalidParse(input:string, expected:any) : void {
|
||||
var errors : string[] = [];
|
||||
var actual = parse(input, errors);
|
||||
function assertInvalidParse(input:string, expected:any, options?: ParseOptions) : void {
|
||||
var errors : {error: ParseErrorCode}[] = [];
|
||||
var actual = parse(input, errors, options);
|
||||
|
||||
assert(errors.length > 0);
|
||||
assert.deepEqual(actual, expected);
|
||||
}
|
||||
|
||||
function assertTree(input:string, expected:any) : void {
|
||||
var errors : ParseError[] = [];
|
||||
var actual = parseTree(input, errors);
|
||||
|
||||
assert.equal(errors.length, 0);
|
||||
let checkParent = (node: Node) => {
|
||||
if (node.children) {
|
||||
for (let child of node.children) {
|
||||
assert.equal(node, child.parent);
|
||||
delete child.parent; // delete to avoid recursion in deep equal
|
||||
checkParent(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkParent(actual);
|
||||
|
||||
assert.deepEqual(actual, expected);
|
||||
}
|
||||
|
||||
function assertNodeAtLocation(input:Node, segments: Segment[], expected: any) {
|
||||
let actual = findNodeAtLocation(input, segments);
|
||||
assert.deepEqual(actual ? getNodeValue(actual) : void 0, expected);
|
||||
}
|
||||
|
||||
|
||||
function assertLocation(input:string, expectedSegments: Segment[], expectedNodeType: string, expectedCompleteProperty: boolean) : void {
|
||||
var errors : {error: ParseErrorCode}[] = [];
|
||||
var offset = input.indexOf('|');
|
||||
input = input.substring(0, offset) + input.substring(offset+1, input.length);
|
||||
var actual = getLocation(input, offset);
|
||||
assert(actual);
|
||||
assert.deepEqual(actual.path, expectedSegments, input);
|
||||
assert.equal(actual.previousNode && actual.previousNode.type, expectedNodeType, input);
|
||||
assert.equal(actual.isAtPropertyKey, expectedCompleteProperty, input);
|
||||
}
|
||||
|
||||
suite('JSON', () => {
|
||||
test('tokens', () => {
|
||||
assertKinds('{', SyntaxKind.OpenBraceToken);
|
||||
|
@ -147,7 +184,7 @@ suite('JSON', () => {
|
|||
assertValidParse('23e3', 23e3);
|
||||
assertValidParse('1.2E+3', 1.2E+3);
|
||||
assertValidParse('1.2E-3', 1.2E-3);
|
||||
|
||||
assertValidParse('1.2E-3 // comment', 1.2E-3);
|
||||
});
|
||||
|
||||
test('parse: objects', () => {
|
||||
|
@ -157,6 +194,9 @@ suite('JSON', () => {
|
|||
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
|
||||
assertValidParse('{ "a": false, "b": true, "c": [ 7.4 ] }', { a: false, b: true, c: [ 7.4 ]});
|
||||
assertValidParse('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }', { lineComment: '//', blockComment: ["/*", "*/"], brackets: [ ["{", "}"], ["[", "]"], ["(", ")"] ] });
|
||||
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
|
||||
assertValidParse('{ "hello": { "again": { "inside": 5 }, "world": 1 }}', { hello: { again: { inside: 5 }, world: 1 }});
|
||||
assertValidParse('{ "foo": /*hello*/true }', { foo: true });
|
||||
});
|
||||
|
||||
test('parse: arrays', () => {
|
||||
|
@ -183,4 +223,105 @@ suite('JSON', () => {
|
|||
assertInvalidParse('[ ,1, 2, 3 ]', [ 1, 2, 3 ]);
|
||||
assertInvalidParse('[ ,1, 2, 3, ]', [ 1, 2, 3 ]);
|
||||
});
|
||||
|
||||
test('parse: disallow commments', () => {
|
||||
let options = { disallowComments: true };
|
||||
|
||||
assertValidParse('[ 1, 2, null, "foo" ]', [ 1, 2, null, "foo"], options);
|
||||
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
|
||||
|
||||
assertInvalidParse('{ "foo": /*comment*/ true }', { foo: true }, options);
|
||||
});
|
||||
|
||||
test('location: properties', () => {
|
||||
assertLocation('|{ "foo": "bar" }', [], void 0, false);
|
||||
assertLocation('{| "foo": "bar" }', [], void 0, true);
|
||||
assertLocation('{ |"foo": "bar" }', ["foo" ], "property", true);
|
||||
assertLocation('{ "foo|": "bar" }', [ "foo" ], "property", true);
|
||||
assertLocation('{ "foo"|: "bar" }', ["foo" ], "property", true);
|
||||
assertLocation('{ "foo": "bar"| }', ["foo" ], "string", false);
|
||||
assertLocation('{ "foo":| "bar" }', ["foo" ], void 0, false);
|
||||
assertLocation('{ "foo": {"bar|": 1, "car": 2 } }', ["foo", "bar" ], "property", true);
|
||||
assertLocation('{ "foo": {"bar": 1|, "car": 3 } }', ["foo", "bar" ], "number", false);
|
||||
assertLocation('{ "foo": {"bar": 1,| "car": 4 } }', ["foo"], void 0, true);
|
||||
assertLocation('{ "foo": {"bar": 1, "ca|r": 5 } }', ["foo", "car" ], "property", true);
|
||||
assertLocation('{ "foo": {"bar": 1, "car": 6| } }', ["foo", "car" ], "number", false);
|
||||
assertLocation('{ "foo": {"bar": 1, "car": 7 }| }', ["foo"], void 0, false);
|
||||
assertLocation('{ "foo": {"bar": 1, "car": 8 },| "goo": {} }', [], void 0, true);
|
||||
assertLocation('{ "foo": {"bar": 1, "car": 9 }, "go|o": {} }', ["goo" ], "property", true);
|
||||
assertLocation('{ "dep": {"bar": 1, "car": |', ["dep", "car" ], void 0, false);
|
||||
assertLocation('{ "dep": {"bar": 1,, "car": |', ["dep", "car" ], void 0, false);
|
||||
assertLocation('{ "dep": {"bar": "na", "dar": "ma", "car": | } }', ["dep", "car" ], void 0, false);
|
||||
});
|
||||
|
||||
test('location: arrays', () => {
|
||||
assertLocation('|["foo", null ]', [], void 0, false);
|
||||
assertLocation('[|"foo", null ]', [0], "string", false);
|
||||
assertLocation('["foo"|, null ]', [0], "string", false);
|
||||
assertLocation('["foo",| null ]', [1], void 0, false);
|
||||
assertLocation('["foo", |null ]', [1], "null", false);
|
||||
assertLocation('["foo", null,| ]', [2], void 0, false);
|
||||
assertLocation('["foo", null,,| ]', [3], void 0, false);
|
||||
assertLocation('[["foo", null,, ],|', [1], void 0, false);
|
||||
});
|
||||
|
||||
test('tree: literals', () => {
|
||||
assertTree('true', { type: 'boolean', offset: 0, length: 4, value: true });
|
||||
assertTree('false', { type: 'boolean', offset: 0, length: 5, value: false });
|
||||
assertTree('null', { type: 'null', offset: 0, length: 4, value: null });
|
||||
assertTree('23', { type: 'number', offset: 0, length: 2, value: 23 });
|
||||
assertTree('-1.93e-19', { type: 'number', offset: 0, length: 9, value: -1.93e-19 });
|
||||
assertTree('"hello"', { type: 'string', offset: 0, length: 7, value: 'hello' });
|
||||
});
|
||||
|
||||
test('tree: arrays', () => {
|
||||
assertTree('[]', { type: 'array', offset: 0, length: 2, children: [] });
|
||||
assertTree('[ 1 ]', { type: 'array', offset: 0, length: 5, children: [{ type: 'number', offset: 2, length: 1, value: 1 }] });
|
||||
assertTree('[ 1,"x"]', { type: 'array', offset: 0, length: 8, children: [
|
||||
{ type: 'number', offset: 2, length: 1, value: 1 },
|
||||
{ type: 'string', offset: 4, length: 3, value: 'x' }
|
||||
]});
|
||||
assertTree('[[]]', { type: 'array', offset: 0, length: 4, children: [
|
||||
{ type: 'array', offset: 1, length: 2, children: []}
|
||||
]});
|
||||
});
|
||||
|
||||
test('tree: objects', () => {
|
||||
assertTree('{ }', { type: 'object', offset: 0, length: 3, children: [] });
|
||||
assertTree('{ "val": 1 }', { type: 'object', offset: 0, length: 12, children: [
|
||||
{ type: 'property', offset: 2, length: 8, columnOffset: 7, children: [
|
||||
{ type: 'string', offset: 2, length: 5, value: 'val' },
|
||||
{ type: 'number', offset: 9, length: 1, value: 1 }
|
||||
]}
|
||||
]});
|
||||
assertTree('{"id": "$", "v": [ null, null] }',
|
||||
{ type: 'object', offset: 0, length: 32, children: [
|
||||
{ type: 'property', offset: 1, length: 9, columnOffset: 5, children: [
|
||||
{ type: 'string', offset: 1, length: 4, value: 'id' },
|
||||
{ type: 'string', offset: 7, length: 3, value: '$' }
|
||||
]},
|
||||
{ type: 'property', offset: 12, length: 19, columnOffset: 15, children: [
|
||||
{ type: 'string', offset: 12, length: 3, value: 'v' },
|
||||
{ type: 'array', offset: 17, length: 13, children: [
|
||||
{ type: 'null', offset: 19, length: 4, value: null },
|
||||
{ type: 'null', offset: 25, length: 4, value: null }
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
);
|
||||
});
|
||||
|
||||
test('tree: find location', () => {
|
||||
let root = parseTree('{ "key1": { "key11": [ "val111", "val112" ] }, "key2": [ { "key21": false, "key22": 221 }, null, [{}] ] }');
|
||||
assertNodeAtLocation(root, [ "key1"], { key11: [ 'val111', 'val112' ]});
|
||||
assertNodeAtLocation(root, [ "key1", "key11"], [ 'val111', 'val112' ]);
|
||||
assertNodeAtLocation(root, [ "key1", "key11", 0], 'val111');
|
||||
assertNodeAtLocation(root, [ "key1", "key11", 1], 'val112');
|
||||
assertNodeAtLocation(root, [ "key1", "key11", 2], void 0);
|
||||
assertNodeAtLocation(root, [ "key2", 0, "key21"], false);
|
||||
assertNodeAtLocation(root, [ "key2", 0, "key22"], 221);
|
||||
assertNodeAtLocation(root, [ "key2", 1], null);
|
||||
assertNodeAtLocation(root, [ "key2", 2], [{}]);
|
||||
assertNodeAtLocation(root, [ "key2", 2, 0], {});
|
||||
});
|
||||
});
|
||||
|
|
90
src/vs/base/test/common/jsonEdit.test.ts
Normal file
90
src/vs/base/test/common/jsonEdit.test.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 json = require('vs/base/common/json');
|
||||
import {FormattingOptions, Edit} from 'vs/base/common/jsonFormatter';
|
||||
import {setProperty, removeProperty} from 'vs/base/common/jsonEdit';
|
||||
import assert = require('assert');
|
||||
|
||||
suite('JSON - edits', () => {
|
||||
|
||||
function assertEdit(content: string, edits: Edit[], expected: string) {
|
||||
assert(edits);
|
||||
let lastEditOffset = content.length;
|
||||
for (let i = edits.length - 1; i >= 0; i--) {
|
||||
let edit = edits[i];
|
||||
assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length)
|
||||
assert(typeof edit.content === 'string');
|
||||
assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered
|
||||
lastEditOffset = edit.offset;
|
||||
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
|
||||
}
|
||||
assert.equal(content, expected);
|
||||
}
|
||||
|
||||
let formatterOptions : FormattingOptions = {
|
||||
insertSpaces: true,
|
||||
tabSize: 2,
|
||||
eol: '\n'
|
||||
}
|
||||
|
||||
test('set property', () => {
|
||||
let content = '{\n "x": "y"\n}';
|
||||
let edits = setProperty(content, ['x'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": "bar"\n}');
|
||||
});
|
||||
|
||||
test('insert property', () => {
|
||||
let content = "{}";
|
||||
let edits = setProperty(content, ['foo'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "foo": "bar"\n}');
|
||||
|
||||
content = "{\n}";
|
||||
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "foo": "bar"\n}');
|
||||
|
||||
content = " {\n }";
|
||||
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, ' {\n "foo": "bar"\n }');
|
||||
|
||||
content = '{\n "x": "y"\n}';
|
||||
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": "y",\n "foo": "bar"\n}');
|
||||
|
||||
edits = setProperty(content, ['x'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": "bar"\n}');
|
||||
|
||||
content = '{\n "x": {\n "a": 1,\n "b": true\n }\n}\n';
|
||||
edits = setProperty(content, ['x'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": "bar"\n}\n');
|
||||
|
||||
edits = setProperty(content, ['x', 'b'], 'bar', formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "b": "bar"\n }\n}\n');
|
||||
|
||||
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 0);
|
||||
assertEdit(content, edits, '{\n "x": {\n "c": "bar",\n "a": 1,\n "b": true\n }\n}\n');
|
||||
|
||||
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 1);
|
||||
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "c": "bar",\n "b": true\n }\n}\n');
|
||||
|
||||
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 2);
|
||||
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "b": true,\n "c": "bar"\n }\n}\n');
|
||||
});
|
||||
|
||||
test('remove property', () => {
|
||||
let content = '{\n "x": "y"\n}';
|
||||
let edits = removeProperty(content, ['x'], formatterOptions);
|
||||
assertEdit(content, edits, '{}');
|
||||
|
||||
content = '{\n "x": "y", "a": []\n}';
|
||||
edits = removeProperty(content, ['x'], formatterOptions);
|
||||
assertEdit(content, edits, '{\n "a": []\n}');
|
||||
|
||||
content = '{\n "x": "y", "a": []\n}';
|
||||
edits = removeProperty(content, ['a'], formatterOptions);
|
||||
assertEdit(content, edits, '{\n "x": "y"\n}');
|
||||
});
|
||||
});
|
|
@ -4,46 +4,33 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import {Model} from 'vs/editor/common/model/model';
|
||||
import ModesTestUtils = require('vs/editor/test/common/modesTestUtils');
|
||||
import Formatter = require('vs/languages/json/common/features/jsonFormatter');
|
||||
import MirrorModel = require('vs/editor/common/model/mirrorModel');
|
||||
import Formatter = require('vs/base/common/jsonFormatter');
|
||||
import assert = require('assert');
|
||||
|
||||
suite('JSON - formatter', () => {
|
||||
|
||||
function format(unformatted: string, expected: string, insertSpaces = true) {
|
||||
var range : EditorCommon.IRange = null;
|
||||
|
||||
var mirrorModel = MirrorModel.createTestMirrorModelFromString(unformatted);
|
||||
|
||||
var rangeStart = unformatted.indexOf('|');
|
||||
var rangeEnd = unformatted.lastIndexOf('|');
|
||||
function format(content: string, expected: string, insertSpaces = true) {
|
||||
let range = void 0;
|
||||
var rangeStart = content.indexOf('|');
|
||||
var rangeEnd = content.lastIndexOf('|');
|
||||
if (rangeStart !== -1 && rangeEnd !== -1) {
|
||||
unformatted = unformatted.substring(0, rangeStart) + unformatted.substring(rangeStart + 1, rangeEnd) + unformatted.substring(rangeEnd + 1);
|
||||
|
||||
var startPos = mirrorModel.getPositionFromOffset(rangeStart);
|
||||
var endPos = mirrorModel.getPositionFromOffset(rangeEnd);
|
||||
range = { startLineNumber: startPos.lineNumber, startColumn: startPos.column, endLineNumber: endPos.lineNumber, endColumn: endPos.column };
|
||||
mirrorModel = MirrorModel.createTestMirrorModelFromString(unformatted);
|
||||
content = content.substring(0, rangeStart) + content.substring(rangeStart + 1, rangeEnd) + content.substring(rangeEnd + 1);
|
||||
range = { offset: rangeStart, length: rangeEnd - rangeStart };
|
||||
}
|
||||
|
||||
var operations = Formatter.format(mirrorModel, range, { tabSize: 2, insertSpaces: insertSpaces });
|
||||
var edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' });
|
||||
|
||||
var model = new Model(unformatted, Model.DEFAULT_CREATION_OPTIONS, null);
|
||||
model.pushEditOperations([], operations.map(o => {
|
||||
return {
|
||||
range: Range.lift(o.range),
|
||||
text: o.text,
|
||||
identifier: null,
|
||||
forceMoveMarkers: false
|
||||
};
|
||||
}), () => []);
|
||||
var newContent = model.getValue(EditorCommon.EndOfLinePreference.LF);
|
||||
assert.equal(newContent, expected);
|
||||
model.dispose();
|
||||
let lastEditOffset = content.length;
|
||||
for (let i = edits.length - 1; i >= 0; i--) {
|
||||
let edit = edits[i];
|
||||
assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length)
|
||||
assert(typeof edit.content === 'string');
|
||||
assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered
|
||||
lastEditOffset = edit.offset;
|
||||
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
|
||||
}
|
||||
|
||||
assert.equal(content, expected);
|
||||
}
|
||||
|
||||
test('object - single property', () => {
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { AIAdapter } from 'vs/base/node/aiAdapter';
|
||||
import {AIAdapter} from 'vs/base/parts/ai/node/aiAdapter';
|
||||
|
||||
interface IAppInsightsEvent {
|
||||
eventName: string;
|
||||
|
@ -21,9 +21,9 @@ class AppInsightsMock {
|
|||
|
||||
public trackEvent(eventName: string, properties?: { string?: string; }, measurements?: { string?: number; }): void {
|
||||
this.events.push({
|
||||
eventName: eventName,
|
||||
properties: properties,
|
||||
measurements: measurements
|
||||
eventName,
|
||||
properties,
|
||||
measurements
|
||||
});
|
||||
}
|
||||
public trackPageView(): void {
|
||||
|
@ -33,6 +33,10 @@ class AppInsightsMock {
|
|||
public trackException(exception: any): void {
|
||||
this.exceptions.push(exception);
|
||||
}
|
||||
|
||||
public sendPendingData(callback): void {
|
||||
// called on dispose
|
||||
}
|
||||
}
|
||||
|
||||
suite('AIAdapter', () => {
|
||||
|
@ -42,7 +46,7 @@ suite('AIAdapter', () => {
|
|||
|
||||
setup(() => {
|
||||
appInsightsMock = new AppInsightsMock();
|
||||
adapter = new AIAdapter(null, prefix, appInsightsMock);
|
||||
adapter = new AIAdapter(prefix, undefined, () => appInsightsMock);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
@ -56,11 +60,16 @@ suite('AIAdapter', () => {
|
|||
assert.equal(appInsightsMock.events[0].eventName, `${prefix}/testEvent`);
|
||||
});
|
||||
|
||||
test('Track UnhandledError as exception and events', () => {
|
||||
var sampleError = new Error('test');
|
||||
test('addional data', () => {
|
||||
adapter = new AIAdapter(prefix, { first: '1st', second: 2, third: true }, () => appInsightsMock);
|
||||
adapter.log('testEvent');
|
||||
|
||||
adapter.logException(sampleError);
|
||||
assert.equal(appInsightsMock.exceptions.length, 1);
|
||||
assert.equal(appInsightsMock.events.length, 1);
|
||||
let [first] = appInsightsMock.events;
|
||||
assert.equal(first.eventName, `${prefix}/testEvent`);
|
||||
assert.equal(first.properties['first'], '1st');
|
||||
assert.equal(first.measurements['second'], '2');
|
||||
assert.equal(first.measurements['third'], 1);
|
||||
});
|
||||
|
||||
test('property limits', () => {
|
||||
|
|
|
@ -19,6 +19,7 @@ exports.collectModules= function() {
|
|||
return [
|
||||
createModuleDescription('vs/code/electron-main/main', []),
|
||||
createModuleDescription('vs/code/node/cli', []),
|
||||
createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']),
|
||||
createModuleDescription('vs/code/node/sharedProcessMain', [])
|
||||
];
|
||||
};
|
|
@ -17,9 +17,9 @@ import * as platform from 'vs/base/common/platform';
|
|||
import URI from 'vs/base/common/uri';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ServiceIdentifier, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import product, { IProductConfiguration } from 'vs/code/node/product';
|
||||
import product, { IProductConfiguration } from 'vs/platform/product';
|
||||
import { parseArgs } from 'vs/code/node/argv';
|
||||
import pkg from 'vs/code/node/package';
|
||||
import pkg from 'vs/platform/package';
|
||||
|
||||
export interface IProcessEnvironment {
|
||||
[key: string]: string;
|
||||
|
@ -46,7 +46,7 @@ export interface ICommandLineArguments {
|
|||
waitForWindowClose?: boolean;
|
||||
}
|
||||
|
||||
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
|
||||
export const IEnvironmentService = createDecorator<IEnvironmentService>('mainEnvironmentService');
|
||||
|
||||
export interface IEnvironmentService {
|
||||
serviceId: ServiceIdentifier<any>;
|
||||
|
|
|
@ -409,10 +409,6 @@ export class VSCodeMenu {
|
|||
|
||||
// Files
|
||||
let files = recentList.files;
|
||||
if (platform.isMacintosh && recentList.files.length > 0) {
|
||||
files = recentList.files.filter(f => recentList.folders.indexOf(f) < 0); // TODO@Ben migration (remove in the future)
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
openRecentMenu.append(__separator__());
|
||||
|
||||
|
@ -828,7 +824,7 @@ function __separator__(): Electron.MenuItem {
|
|||
|
||||
function mnemonicLabel(label: string): string {
|
||||
if (platform.isMacintosh) {
|
||||
return label.replace(/&&/g, ''); // no mnemonic support on mac
|
||||
return label.replace(/\(&&\w\)|&&/g, ''); // no mnemonic support on mac/linux
|
||||
}
|
||||
|
||||
return label.replace(/&&/g, '&');
|
||||
|
|
|
@ -5,57 +5,21 @@
|
|||
|
||||
import * as cp from 'child_process';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import pkg from 'vs/code/node/package';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IEnvironment } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEnvironmentService } from 'vs/code/electron-main/env';
|
||||
import { ISettingsService } from 'vs/code/electron-main/settings';
|
||||
import { IUpdateService } from 'vs/code/electron-main/update-manager';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
const boostrapPath = URI.parse(require.toUrl('bootstrap')).fsPath;
|
||||
|
||||
function getEnvironment(envService: IEnvironmentService, updateManager: IUpdateService): IEnvironment {
|
||||
let configuration: IEnvironment = assign({}, envService.cliArgs);
|
||||
configuration.execPath = process.execPath;
|
||||
configuration.appName = envService.product.nameLong;
|
||||
configuration.appRoot = envService.appRoot;
|
||||
configuration.version = pkg.version;
|
||||
configuration.commitHash = envService.product.commit;
|
||||
configuration.appSettingsHome = envService.appSettingsHome;
|
||||
configuration.appSettingsPath = envService.appSettingsPath;
|
||||
configuration.appKeybindingsPath = envService.appKeybindingsPath;
|
||||
configuration.userExtensionsHome = envService.userExtensionsHome;
|
||||
configuration.isBuilt = envService.isBuilt;
|
||||
configuration.updateFeedUrl = updateManager.feedUrl;
|
||||
configuration.updateChannel = updateManager.channel;
|
||||
configuration.extensionsGallery = envService.product.extensionsGallery;
|
||||
function _spawnSharedProcess(): cp.ChildProcess {
|
||||
const env = assign({}, process.env, {
|
||||
AMD_ENTRYPOINT: 'vs/code/node/sharedProcessMain'
|
||||
});
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
function _spawnSharedProcess(envService: IEnvironmentService, updateManager: IUpdateService, settingsManager: ISettingsService): cp.ChildProcess {
|
||||
// Make sure the nls configuration travels to the shared process.
|
||||
const opts = {
|
||||
env: assign(assign({}, process.env), {
|
||||
AMD_ENTRYPOINT: 'vs/code/node/sharedProcessMain'
|
||||
})
|
||||
};
|
||||
|
||||
const result = cp.fork(boostrapPath, ['--type=SharedProcess'], opts);
|
||||
const result = cp.fork(boostrapPath, ['--type=SharedProcess'], { env });
|
||||
|
||||
// handshake
|
||||
result.once('message', () => {
|
||||
result.send({
|
||||
configuration: {
|
||||
env: getEnvironment(envService, updateManager)
|
||||
},
|
||||
contextServiceOptions: {
|
||||
globalSettings: settingsManager.globalSettings
|
||||
}
|
||||
});
|
||||
});
|
||||
result.once('message', () => result.send('hey'));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -63,10 +27,6 @@ function _spawnSharedProcess(envService: IEnvironmentService, updateManager: IUp
|
|||
let spawnCount = 0;
|
||||
|
||||
export function spawnSharedProcess(accessor: ServicesAccessor): IDisposable {
|
||||
const envService = accessor.get(IEnvironmentService);
|
||||
const updateManager = accessor.get(IUpdateService);
|
||||
const settingsManager = accessor.get(ISettingsService);
|
||||
|
||||
let child: cp.ChildProcess;
|
||||
|
||||
const spawn = () => {
|
||||
|
@ -74,7 +34,7 @@ export function spawnSharedProcess(accessor: ServicesAccessor): IDisposable {
|
|||
return;
|
||||
}
|
||||
|
||||
child = _spawnSharedProcess(envService, updateManager, settingsManager);
|
||||
child = _spawnSharedProcess();
|
||||
child.on('exit', spawn);
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import * as nls from 'vs/nls';
|
|||
import * as paths from 'vs/base/common/paths';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import pkg from 'vs/code/node/package';
|
||||
import pkg from 'vs/platform/package';
|
||||
import { EventEmitter } from 'events';
|
||||
import { IStorageService } from 'vs/code/electron-main/storage';
|
||||
import { IPath, VSCodeWindow, ReadyState, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState } from 'vs/code/electron-main/window';
|
||||
|
@ -789,10 +789,6 @@ export class WindowsManager implements IWindowsService {
|
|||
files = arrays.distinct(files);
|
||||
folders = arrays.distinct(folders);
|
||||
|
||||
if (platform.isMacintosh && files.length > 0) {
|
||||
files = files.filter(f => folders.indexOf(f) < 0); // TODO@Ben migration (remove in the future)
|
||||
}
|
||||
|
||||
// Make sure it is bounded
|
||||
files = files.slice(0, 10);
|
||||
folders = folders.slice(0, 10);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as os from 'os';
|
||||
import * as minimist from 'minimist';
|
||||
import pkg from 'vs/code/node/package';
|
||||
import pkg from 'vs/platform/package';
|
||||
|
||||
export interface ParsedArgs extends minimist.ParsedArgs {
|
||||
help: boolean;
|
||||
|
@ -28,6 +28,9 @@ export interface ParsedArgs extends minimist.ParsedArgs {
|
|||
timestamp: string;
|
||||
debugBrkPluginHost: string;
|
||||
debugPluginHost: string;
|
||||
'list-extensions': boolean;
|
||||
'install-extension': string | string[];
|
||||
'uninstall-extension': string | string[];
|
||||
}
|
||||
|
||||
const options: minimist.Opts = {
|
||||
|
@ -37,7 +40,9 @@ const options: minimist.Opts = {
|
|||
'extensionHomePath',
|
||||
'extensionDevelopmentPath',
|
||||
'extensionTestsPath',
|
||||
'timestamp'
|
||||
'timestamp',
|
||||
'install-extension',
|
||||
'uninstall-extension'
|
||||
],
|
||||
boolean: [
|
||||
'help',
|
||||
|
@ -50,7 +55,8 @@ const options: minimist.Opts = {
|
|||
'performance',
|
||||
'verbose',
|
||||
'logExtensionHostCommunication',
|
||||
'disable-extensions'
|
||||
'disable-extensions',
|
||||
'list-extensions'
|
||||
],
|
||||
alias: {
|
||||
help: 'h',
|
||||
|
@ -89,4 +95,9 @@ ${ indent } window.
|
|||
${ indent }--user-data-dir <dir> Specifies the directory that user data is kept in,
|
||||
${ indent } useful when running as root.
|
||||
${ indent }-v, --version Print version.
|
||||
${ indent }-w, --wait Wait for the window to be closed before returning.`;
|
||||
${ indent }-w, --wait Wait for the window to be closed before returning.
|
||||
${ indent }--list-extensions List the installed extensions.
|
||||
${ indent }--install-extension <extension>
|
||||
${ indent } Installs an extension.
|
||||
${ indent }--uninstall-extension <extension>
|
||||
${ indent } Uninstalls an extension.`;
|
||||
|
|
|
@ -4,17 +4,29 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { parseArgs, helpMessage } from 'vs/code/node/argv';
|
||||
import pkg from 'vs/code/node/package';
|
||||
import { parseArgs, helpMessage, ParsedArgs } from 'vs/code/node/argv';
|
||||
import pkg from 'vs/platform/package';
|
||||
|
||||
export function main(args: string[]) {
|
||||
function shouldSpawnCliProcess(argv: ParsedArgs): boolean {
|
||||
return argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension'];
|
||||
}
|
||||
|
||||
interface IMainCli {
|
||||
main: (argv: ParsedArgs) => TPromise<void>;
|
||||
}
|
||||
|
||||
export function main(args: string[]): TPromise<void> {
|
||||
const argv = parseArgs(args);
|
||||
|
||||
if (argv.help) {
|
||||
console.log(helpMessage);
|
||||
} else if (argv.version) {
|
||||
console.log(pkg.version);
|
||||
} else if (shouldSpawnCliProcess(argv)) {
|
||||
const mainCli = new TPromise<IMainCli>(c => require(['vs/code/node/cliProcessMain'], c));
|
||||
return mainCli.then(cli => cli.main(argv));
|
||||
} else {
|
||||
const env = assign({}, process.env, {
|
||||
// this will signal Code that it was spawned from this module
|
||||
|
@ -30,12 +42,16 @@ export function main(args: string[]) {
|
|||
});
|
||||
|
||||
if (argv.wait) {
|
||||
child.on('exit', process.exit);
|
||||
return;
|
||||
return new TPromise<void>(c => child.once('exit', ()=> c(null)));
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
main(process.argv.slice(2));
|
||||
main(process.argv.slice(2))
|
||||
.then(() => process.exit(0))
|
||||
.then(null, err => {
|
||||
console.error(err.stack ? err.stack : err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
122
src/vs/code/node/cliProcessMain.ts
Normal file
122
src/vs/code/node/cliProcessMain.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { ParsedArgs } from 'vs/code/node/argv';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { IEventService } from 'vs/platform/event/common/event';
|
||||
import { EventService } from 'vs/platform/event/common/eventService';
|
||||
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { NodeRequestService } from 'vs/platform/request/node/nodeRequestService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { NodeConfigurationService } from 'vs/platform/configuration/node/nodeConfigurationService';
|
||||
|
||||
const notFound = id => localize('notFound', "Extension '{0}' not found.", id);
|
||||
const notInstalled = id => localize('notInstalled', "Extension '{0}' is not installed.", id);
|
||||
const useId = localize('useId', "Make sure you use the full extension id, eg: {0}", 'ms-vscode.csharp');
|
||||
|
||||
class Main {
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService
|
||||
) {}
|
||||
|
||||
run(argv: ParsedArgs): TPromise<any> {
|
||||
// TODO@joao - make this contributable
|
||||
|
||||
if (argv['list-extensions']) {
|
||||
return this.listExtensions();
|
||||
} else if (argv['install-extension']) {
|
||||
const arg = argv['install-extension'];
|
||||
const ids: string[] = typeof arg === 'string' ? [arg] : arg;
|
||||
return this.installExtension(ids);
|
||||
} else if (argv['uninstall-extension']) {
|
||||
const arg = argv['uninstall-extension'];
|
||||
const ids: string[] = typeof arg === 'string' ? [arg] : arg;
|
||||
return this.uninstallExtension(ids);
|
||||
}
|
||||
}
|
||||
|
||||
private listExtensions(): TPromise<any> {
|
||||
return this.extensionManagementService.getInstalled().then(extensions => {
|
||||
extensions.forEach(e => console.log(getExtensionId(e)));
|
||||
});
|
||||
}
|
||||
|
||||
private installExtension(ids: string[]): TPromise<any> {
|
||||
return this.extensionManagementService.getInstalled().then(installed => {
|
||||
return sequence(ids.map(id => () => {
|
||||
|
||||
const isInstalled = installed.some(e => getExtensionId(e) === id);
|
||||
|
||||
if (isInstalled) {
|
||||
return TPromise.wrapError(localize('alreadyInstalled', "Extension '{0}' is already installed.", id));
|
||||
}
|
||||
|
||||
return this.extensionGalleryService.query({ ids: [id] }).then(result => {
|
||||
const [extension] = result.firstPage;
|
||||
|
||||
if (!extension) {
|
||||
return TPromise.wrapError(`${ notFound(id) }\n${ useId }`);
|
||||
}
|
||||
|
||||
console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id));
|
||||
console.log(localize('installing', "Installing..."));
|
||||
|
||||
return this.extensionManagementService.install(extension).then(extension => {
|
||||
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version));
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private uninstallExtension(ids: string[]): TPromise<any> {
|
||||
return sequence(ids.map(id => () => {
|
||||
return this.extensionManagementService.getInstalled().then(installed => {
|
||||
const [extension] = installed.filter(e => getExtensionId(e) === id);
|
||||
|
||||
if (!extension) {
|
||||
return TPromise.wrapError(`${ notInstalled(id) }\n${ useId }`);
|
||||
}
|
||||
|
||||
console.log(localize('uninstalling', "Uninstalling {0}...", id));
|
||||
|
||||
return this.extensionManagementService.uninstall(extension).then(() => {
|
||||
console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id));
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export function main(argv: ParsedArgs): TPromise<void> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
services.set(IEventService, new SyncDescriptor(EventService));
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService));
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
services.set(IConfigurationService, new SyncDescriptor(NodeConfigurationService));
|
||||
services.set(IRequestService, new SyncDescriptor(NodeRequestService));
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
|
||||
const instantiationService: IInstantiationService = new InstantiationService(services);
|
||||
const main = instantiationService.createInstance(Main);
|
||||
return main.run(argv);
|
||||
}
|
|
@ -7,16 +7,17 @@ import * as fs from 'fs';
|
|||
import * as platform from 'vs/base/common/platform';
|
||||
import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IConfiguration } from 'vs/platform/workspace/common/workspace';
|
||||
import { WorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
|
||||
import { registerAIChannel } from 'vs/base/parts/ai/node/ai.app';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { IEventService } from 'vs/platform/event/common/event';
|
||||
import { EventService } from 'vs/platform/event/common/eventService';
|
||||
import { ExtensionsChannel } from 'vs/workbench/parts/extensions/common/extensionsIpc';
|
||||
import { ExtensionsService } from 'vs/workbench/parts/extensions/node/extensionsService';
|
||||
|
||||
interface IInitData {
|
||||
configuration: IConfiguration;
|
||||
contextServiceOptions: { settings: any };
|
||||
}
|
||||
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
|
||||
function quit(err?: Error) {
|
||||
if (err) {
|
||||
|
@ -39,16 +40,24 @@ function setupPlanB(parentPid: number): void {
|
|||
}, 5000);
|
||||
}
|
||||
|
||||
function main(server: Server, initData: IInitData): void {
|
||||
const eventService = new EventService();
|
||||
const contextService = new WorkspaceContextService(eventService, null, initData.configuration, initData.contextServiceOptions);
|
||||
const extensionService = new ExtensionsService(contextService);
|
||||
const channel = new ExtensionsChannel(extensionService);
|
||||
function main(server: Server): void {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
server.registerChannel('extensions', channel);
|
||||
services.set(IEventService, new SyncDescriptor(EventService));
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService));
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
|
||||
// eventually clean up old extensions
|
||||
setTimeout(() => extensionService.removeDeprecatedExtensions(), 5000);
|
||||
const instantiationService = new InstantiationService(services);
|
||||
|
||||
instantiationService.invokeFunction(accessor => {
|
||||
const extensionManagementService = accessor.get(IExtensionManagementService);
|
||||
const channel = new ExtensionManagementChannel(extensionManagementService);
|
||||
server.registerChannel('extensions', channel);
|
||||
registerAIChannel(server);
|
||||
|
||||
// eventually clean up old extensions
|
||||
setTimeout(() => (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
function setupIPC(hook: string): TPromise<Server> {
|
||||
|
@ -85,8 +94,8 @@ function setupIPC(hook: string): TPromise<Server> {
|
|||
return setup(true);
|
||||
}
|
||||
|
||||
function handshake(): TPromise<IInitData> {
|
||||
return new TPromise<IInitData>((c, e) => {
|
||||
function handshake(): TPromise<void> {
|
||||
return new TPromise<void>((c, e) => {
|
||||
process.once('message', c);
|
||||
process.once('error', e);
|
||||
process.send('hello');
|
||||
|
@ -94,6 +103,6 @@ function handshake(): TPromise<IInitData> {
|
|||
}
|
||||
|
||||
TPromise.join<any>([setupIPC(process.env['VSCODE_SHARED_IPC_HOOK']), handshake()])
|
||||
.then(r => main(r[0], r[1]))
|
||||
.then(r => main(r[0]))
|
||||
.then(() => setupPlanB(process.env['VSCODE_PID']))
|
||||
.done(null, quit);
|
|
@ -13,8 +13,6 @@
|
|||
*---------------------------------------------------------------------------------------------
|
||||
*---------------------------------------------------------------------------------------------
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/// <reference path="declares.ts" />
|
||||
/// <reference path="loader.ts" />
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || function (d, b) {
|
||||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
||||
|
@ -100,7 +98,7 @@ var CSSLoaderPlugin;
|
|||
this._insertLinkNode(linkNode);
|
||||
};
|
||||
return BrowserCSSLoader;
|
||||
})();
|
||||
}());
|
||||
/**
|
||||
* Prior to IE10, IE could not go above 31 stylesheets in a page
|
||||
* http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
|
||||
|
@ -201,7 +199,7 @@ var CSSLoaderPlugin;
|
|||
}
|
||||
};
|
||||
return IE9CSSLoader;
|
||||
})(BrowserCSSLoader);
|
||||
}(BrowserCSSLoader));
|
||||
var IE8CSSLoader = (function (_super) {
|
||||
__extends(IE8CSSLoader, _super);
|
||||
function IE8CSSLoader() {
|
||||
|
@ -214,7 +212,7 @@ var CSSLoaderPlugin;
|
|||
};
|
||||
};
|
||||
return IE8CSSLoader;
|
||||
})(IE9CSSLoader);
|
||||
}(IE9CSSLoader));
|
||||
var NodeCSSLoader = (function () {
|
||||
function NodeCSSLoader() {
|
||||
this.fs = require.nodeRequire('fs');
|
||||
|
@ -229,7 +227,7 @@ var CSSLoaderPlugin;
|
|||
};
|
||||
NodeCSSLoader.BOM_CHAR_CODE = 65279;
|
||||
return NodeCSSLoader;
|
||||
})();
|
||||
}());
|
||||
// ------------------------------ Finally, the plugin
|
||||
var CSSPlugin = (function () {
|
||||
function CSSPlugin(cssLoader) {
|
||||
|
@ -279,7 +277,7 @@ var CSSLoaderPlugin;
|
|||
};
|
||||
CSSPlugin.BUILD_MAP = {};
|
||||
return CSSPlugin;
|
||||
})();
|
||||
}());
|
||||
CSSLoaderPlugin.CSSPlugin = CSSPlugin;
|
||||
var Utilities = (function () {
|
||||
function Utilities() {
|
||||
|
@ -411,7 +409,7 @@ var CSSLoaderPlugin;
|
|||
});
|
||||
};
|
||||
return Utilities;
|
||||
})();
|
||||
}());
|
||||
CSSLoaderPlugin.Utilities = Utilities;
|
||||
(function () {
|
||||
var cssLoader = null;
|
||||
|
|
|
@ -207,15 +207,15 @@ class CSSBasedConfiguration extends Disposable {
|
|||
maxDigitWidth = Math.max(maxDigitWidth, digits[i].width);
|
||||
}
|
||||
|
||||
return new FontInfo(
|
||||
bareFontInfo.fontFamily,
|
||||
bareFontInfo.fontSize,
|
||||
bareFontInfo.lineHeight,
|
||||
typicalHalfwidthCharacter.width,
|
||||
typicalFullwidthCharacter.width,
|
||||
space.width,
|
||||
maxDigitWidth
|
||||
);
|
||||
return new FontInfo({
|
||||
fontFamily: bareFontInfo.fontFamily,
|
||||
fontSize: bareFontInfo.fontSize,
|
||||
lineHeight: bareFontInfo.lineHeight,
|
||||
typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width,
|
||||
typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width,
|
||||
spaceWidth: space.width,
|
||||
maxDigitWidth: maxDigitWidth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +241,8 @@ export class Configuration extends CommonEditorConfiguration {
|
|||
if (this._configWithDefaults.getEditorOptions().automaticLayout) {
|
||||
this._elementSizeObserver.startObserving();
|
||||
}
|
||||
|
||||
this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions()));
|
||||
}
|
||||
|
||||
private _onReferenceDomElementSizeChanged(): void {
|
||||
|
@ -287,6 +289,10 @@ export class Configuration extends CommonEditorConfiguration {
|
|||
return this._elementSizeObserver.getHeight();
|
||||
}
|
||||
|
||||
protected _getCanUseTranslate3d(): boolean {
|
||||
return browser.canUseTranslate3d && browser.getZoomLevel() === 0;
|
||||
}
|
||||
|
||||
protected readConfiguration(bareFontInfo:BareFontInfo): FontInfo {
|
||||
return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import {IKeyboardEvent} from 'vs/base/browser/keyboardEvent';
|
|||
import {StyleMutator} from 'vs/base/browser/styleMutator';
|
||||
import {GlobalScreenReaderNVDA} from 'vs/editor/common/config/commonEditorConfig';
|
||||
import {TextAreaHandler} from 'vs/editor/common/controller/textAreaHandler';
|
||||
import {IClipboardEvent, IKeyboardEventWrapper, ITextAreaWrapper, TextAreaStrategy} from 'vs/editor/common/controller/textAreaState';
|
||||
import {IClipboardEvent, ICompositionEvent, IKeyboardEventWrapper, ITextAreaWrapper, TextAreaStrategy} from 'vs/editor/common/controller/textAreaState';
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import {ViewEventHandler} from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
|
@ -114,11 +114,14 @@ class TextAreaWrapper extends Disposable implements ITextAreaWrapper {
|
|||
private _onKeyPress = this._register(new Emitter<IKeyboardEventWrapper>());
|
||||
public onKeyPress: Event<IKeyboardEventWrapper> = this._onKeyPress.event;
|
||||
|
||||
private _onCompositionStart = this._register(new Emitter<void>());
|
||||
public onCompositionStart: Event<void> = this._onCompositionStart.event;
|
||||
private _onCompositionStart = this._register(new Emitter<ICompositionEvent>());
|
||||
public onCompositionStart: Event<ICompositionEvent> = this._onCompositionStart.event;
|
||||
|
||||
private _onCompositionEnd = this._register(new Emitter<void>());
|
||||
public onCompositionEnd: Event<void> = this._onCompositionEnd.event;
|
||||
private _onCompositionUpdate = this._register(new Emitter<ICompositionEvent>());
|
||||
public onCompositionUpdate: Event<ICompositionEvent> = this._onCompositionUpdate.event;
|
||||
|
||||
private _onCompositionEnd = this._register(new Emitter<ICompositionEvent>());
|
||||
public onCompositionEnd: Event<ICompositionEvent> = this._onCompositionEnd.event;
|
||||
|
||||
private _onInput = this._register(new Emitter<void>());
|
||||
public onInput: Event<void> = this._onInput.event;
|
||||
|
@ -139,8 +142,9 @@ class TextAreaWrapper extends Disposable implements ITextAreaWrapper {
|
|||
this._register(dom.addStandardDisposableListener(this._textArea, 'keydown', (e) => this._onKeyDown.fire(new KeyboardEventWrapper(e))));
|
||||
this._register(dom.addStandardDisposableListener(this._textArea, 'keyup', (e) => this._onKeyUp.fire(new KeyboardEventWrapper(e))));
|
||||
this._register(dom.addStandardDisposableListener(this._textArea, 'keypress', (e) => this._onKeyPress.fire(new KeyboardEventWrapper(e))));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'compositionstart', (e) => this._onCompositionStart.fire()));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'compositionend', (e) => this._onCompositionEnd.fire()));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'compositionstart', (e) => this._onCompositionStart.fire(e)));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'compositionupdate', (e) => this._onCompositionUpdate.fire(e)));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'compositionend', (e) => this._onCompositionEnd.fire(e)));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'input', (e) => this._onInput.fire()));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'cut', (e:ClipboardEvent) => this._onCut.fire(new ClipboardEventWrapper(e))));
|
||||
this._register(dom.addDisposableListener(this._textArea, 'copy', (e:ClipboardEvent) => this._onCopy.fire(new ClipboardEventWrapper(e))));
|
||||
|
@ -213,6 +217,8 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
private contentWidth:number;
|
||||
private scrollLeft:number;
|
||||
|
||||
private visibleRange:VisibleRange;
|
||||
|
||||
constructor(context:ViewContext, viewController:IViewController, viewHelper:IKeyboardHandlerHelper) {
|
||||
super();
|
||||
|
||||
|
@ -252,11 +258,11 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
this._context.privateViewEventBus.emit(editorCommon.ViewEventNames.RevealRangeEvent, revealPositionEvent);
|
||||
|
||||
// Find range pixel position
|
||||
let visibleRange = this.viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column);
|
||||
this.visibleRange = this.viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column);
|
||||
|
||||
if (visibleRange) {
|
||||
StyleMutator.setTop(this.textArea.actual, visibleRange.top);
|
||||
StyleMutator.setLeft(this.textArea.actual, this.contentLeft + visibleRange.left - this.scrollLeft);
|
||||
if (this.visibleRange) {
|
||||
StyleMutator.setTop(this.textArea.actual, this.visibleRange.top);
|
||||
StyleMutator.setLeft(this.textArea.actual, this.contentLeft + this.visibleRange.left - this.scrollLeft);
|
||||
}
|
||||
|
||||
if (browser.isIE11orEarlier) {
|
||||
|
@ -267,12 +273,24 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
StyleMutator.setHeight(this.textArea.actual, this._context.configuration.editor.lineHeight);
|
||||
dom.addClass(this.viewHelper.viewDomNode, 'ime-input');
|
||||
}));
|
||||
|
||||
this._toDispose.push(this.textAreaHandler.onCompositionUpdate((e) => {
|
||||
// adjust width by its size
|
||||
let canvasElem = <HTMLCanvasElement>document.createElement('canvas');
|
||||
let context = canvasElem.getContext('2d');
|
||||
context.font = window.getComputedStyle(this.textArea.actual).font;
|
||||
let metrics = context.measureText(e.data);
|
||||
StyleMutator.setWidth(this.textArea.actual, metrics.width);
|
||||
}));
|
||||
|
||||
this._toDispose.push(this.textAreaHandler.onCompositionEnd((e) => {
|
||||
this.textArea.actual.style.height = '';
|
||||
this.textArea.actual.style.width = '';
|
||||
StyleMutator.setLeft(this.textArea.actual, 0);
|
||||
StyleMutator.setTop(this.textArea.actual, 0);
|
||||
dom.removeClass(this.viewHelper.viewDomNode, 'ime-input');
|
||||
|
||||
this.visibleRange = null;
|
||||
}));
|
||||
this._toDispose.push(GlobalScreenReaderNVDA.onChange((value) => {
|
||||
this.textAreaHandler.setStrategy(this._getStrategy());
|
||||
|
@ -293,7 +311,7 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
if (GlobalScreenReaderNVDA.getValue()) {
|
||||
return TextAreaStrategy.NVDA;
|
||||
}
|
||||
if (this._context.configuration.editor.experimentalScreenReader) {
|
||||
if (this._context.configuration.editor.viewInfo.experimentalScreenReader) {
|
||||
return TextAreaStrategy.NVDA;
|
||||
}
|
||||
return TextAreaStrategy.IENarrator;
|
||||
|
@ -308,7 +326,7 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
if (e.fontInfo) {
|
||||
Configuration.applyFontInfoSlow(this.textArea.actual, this._context.configuration.editor.fontInfo);
|
||||
}
|
||||
if (e.experimentalScreenReader) {
|
||||
if (e.viewInfo.experimentalScreenReader) {
|
||||
this.textAreaHandler.setStrategy(this._getStrategy());
|
||||
}
|
||||
return false;
|
||||
|
@ -316,6 +334,10 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
|
||||
public onScrollChanged(e:editorCommon.IScrollEvent): boolean {
|
||||
this.scrollLeft = e.scrollLeft;
|
||||
if (this.visibleRange) {
|
||||
StyleMutator.setTop(this.textArea.actual, this.visibleRange.top);
|
||||
StyleMutator.setLeft(this.textArea.actual, this.contentLeft + this.visibleRange.left - this.scrollLeft);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -335,7 +357,7 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public onLayoutChanged(layoutInfo:editorCommon.IEditorLayoutInfo): boolean {
|
||||
public onLayoutChanged(layoutInfo:editorCommon.EditorLayoutInfo): boolean {
|
||||
this.contentLeft = layoutInfo.contentLeft;
|
||||
this.contentWidth = layoutInfo.contentWidth;
|
||||
return false;
|
||||
|
|
|
@ -179,8 +179,8 @@ export class MouseHandler extends ViewEventHandler implements IDisposable {
|
|||
}
|
||||
|
||||
// --- begin event handlers
|
||||
_layoutInfo:editorCommon.IEditorLayoutInfo;
|
||||
public onLayoutChanged(layoutInfo:editorCommon.IEditorLayoutInfo): boolean {
|
||||
_layoutInfo:editorCommon.EditorLayoutInfo;
|
||||
public onLayoutChanged(layoutInfo:editorCommon.EditorLayoutInfo): boolean {
|
||||
this._layoutInfo = layoutInfo;
|
||||
return false;
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ export class MouseHandler extends ViewEventHandler implements IDisposable {
|
|||
let targetIsContent = (t.type === editorCommon.MouseTargetType.CONTENT_TEXT || t.type === editorCommon.MouseTargetType.CONTENT_EMPTY);
|
||||
let targetIsGutter = (t.type === editorCommon.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorCommon.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorCommon.MouseTargetType.GUTTER_LINE_DECORATIONS);
|
||||
let targetIsLineNumbers = (t.type === editorCommon.MouseTargetType.GUTTER_LINE_NUMBERS);
|
||||
let selectOnLineNumbers = this._context.configuration.editor.selectOnLineNumbers;
|
||||
let selectOnLineNumbers = this._context.configuration.editor.viewInfo.selectOnLineNumbers;
|
||||
let targetIsViewZone = (t.type === editorCommon.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorCommon.MouseTargetType.GUTTER_VIEW_ZONE);
|
||||
|
||||
let shouldHandle = e.leftButton;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import {Position} from 'vs/editor/common/core/position';
|
||||
import {Range as EditorRange} from 'vs/editor/common/core/range';
|
||||
import {IEditorLayoutInfo, IEditorPosition, IEditorRange, IPosition, MouseTargetType} from 'vs/editor/common/editorCommon';
|
||||
import {EditorLayoutInfo, IEditorPosition, IEditorRange, IPosition, MouseTargetType} from 'vs/editor/common/editorCommon';
|
||||
import {ClassNames, IMouseTarget, IViewZoneData} from 'vs/editor/browser/editorBrowser';
|
||||
import {IDomNodePosition} from 'vs/base/browser/dom';
|
||||
import {ViewContext} from 'vs/editor/common/view/viewContext';
|
||||
|
@ -177,7 +177,7 @@ export class MouseTargetFactory {
|
|||
return false;
|
||||
}
|
||||
|
||||
public createMouseTarget(layoutInfo:IEditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent, testEventTarget:boolean): IMouseTarget {
|
||||
public createMouseTarget(layoutInfo:EditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent, testEventTarget:boolean): IMouseTarget {
|
||||
try {
|
||||
var r = this._unsafeCreateMouseTarget(layoutInfo, editorContent, e, testEventTarget);
|
||||
return r;
|
||||
|
@ -186,7 +186,7 @@ export class MouseTargetFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private _unsafeCreateMouseTarget(layoutInfo:IEditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent, testEventTarget:boolean): IMouseTarget {
|
||||
private _unsafeCreateMouseTarget(layoutInfo:EditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent, testEventTarget:boolean): IMouseTarget {
|
||||
var mouseVerticalOffset = Math.max(0, this._viewHelper.getScrollTop() + (e.posy - editorContent.top));
|
||||
var mouseContentHorizontalOffset = this._viewHelper.getScrollLeft() + (e.posx - editorContent.left) - layoutInfo.contentLeft;
|
||||
let mouseColumn = this._getMouseColumn(mouseContentHorizontalOffset);
|
||||
|
@ -213,9 +213,9 @@ export class MouseTargetFactory {
|
|||
|
||||
// Is it the textarea cover?
|
||||
if (REGEX.IS_TEXTAREA_COVER.test(path)) {
|
||||
if (this._context.configuration.editor.glyphMargin) {
|
||||
if (this._context.configuration.editor.viewInfo.glyphMargin) {
|
||||
return this.createMouseTargetFromGlyphMargin(t, mouseVerticalOffset, mouseColumn);
|
||||
} else if (this._context.configuration.editor.lineNumbers) {
|
||||
} else if (this._context.configuration.editor.viewInfo.lineNumbers) {
|
||||
return this.createMouseTargetFromLineNumbers(t, mouseVerticalOffset, mouseColumn);
|
||||
} else {
|
||||
return this.createMouseTargetFromLinesDecorationsChild(t, mouseVerticalOffset, mouseColumn);
|
||||
|
@ -359,8 +359,8 @@ export class MouseTargetFactory {
|
|||
if (adjustedPosy <= editorContent.top) {
|
||||
adjustedPosy = editorContent.top + 1;
|
||||
}
|
||||
if (adjustedPosy >= editorContent.top + this._context.configuration.editor.observedOuterHeight) {
|
||||
adjustedPosy = editorContent.top + this._context.configuration.editor.observedOuterHeight - 1;
|
||||
if (adjustedPosy >= editorContent.top + this._context.configuration.editor.layoutInfo.height) {
|
||||
adjustedPosy = editorContent.top + this._context.configuration.editor.layoutInfo.height - 1;
|
||||
}
|
||||
|
||||
var hitx = e.posx - document.body.scrollLeft;
|
||||
|
@ -594,7 +594,7 @@ export class MouseTargetFactory {
|
|||
};
|
||||
}
|
||||
|
||||
public getMouseColumn(layoutInfo:IEditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent): number {
|
||||
public getMouseColumn(layoutInfo:EditorLayoutInfo, editorContent:IDomNodePosition, e:ISimplifiedMouseEvent): number {
|
||||
let mouseContentHorizontalOffset = this._viewHelper.getScrollLeft() + (e.posx - editorContent.left) - layoutInfo.contentLeft;
|
||||
return this._getMouseColumn(mouseContentHorizontalOffset);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import 'vs/editor/contrib/outlineMarker/browser/outlineMarker';
|
|||
import 'vs/editor/contrib/parameterHints/browser/parameterHints';
|
||||
import 'vs/editor/contrib/quickFix/browser/quickFix';
|
||||
import 'vs/editor/contrib/referenceSearch/browser/referenceSearch';
|
||||
import 'vs/editor/contrib/rename/browser/rename2';
|
||||
import 'vs/editor/contrib/rename/browser/rename';
|
||||
import 'vs/editor/contrib/smartSelect/common/smartSelect';
|
||||
import 'vs/editor/contrib/smartSelect/common/jumpToBracket';
|
||||
import 'vs/editor/contrib/snippet/common/snippet';
|
||||
|
@ -40,7 +40,6 @@ import 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode';
|
|||
import 'vs/editor/contrib/toggleWordWrap/common/toggleWordWrap';
|
||||
import 'vs/css!vs/editor/contrib/wordHighlighter/browser/wordHighlighter';
|
||||
import 'vs/editor/contrib/wordHighlighter/common/wordHighlighter';
|
||||
import 'vs/editor/contrib/workerStatusReporter/browser/workerStatusReporter';
|
||||
import 'vs/editor/contrib/defineKeybinding/browser/defineKeybinding';
|
||||
import 'vs/editor/contrib/folding/browser/folding';
|
||||
import 'vs/editor/contrib/indentation/common/indentation';
|
||||
|
|
|
@ -459,7 +459,7 @@ export interface IOverviewRuler {
|
|||
getDomNode(): HTMLElement;
|
||||
dispose(): void;
|
||||
setZones(zones:OverviewRulerZone[]): void;
|
||||
setLayout(position:editorCommon.IOverviewRulerPosition): void;
|
||||
setLayout(position:editorCommon.OverviewRulerPosition): void;
|
||||
}
|
||||
/**
|
||||
* A rich code editor.
|
||||
|
|
|
@ -139,7 +139,7 @@ export class SimpleEditorService implements IEditorService {
|
|||
|
||||
private findModel(editor:editorCommon.ICommonCodeEditor, data:IResourceInput): editorCommon.IModel {
|
||||
var model = editor.getModel();
|
||||
if(model.getAssociatedResource().toString() !== data.resource.toString()) {
|
||||
if(model.uri.toString() !== data.resource.toString()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import {RemoteTelemetryServiceHelper} from 'vs/platform/telemetry/common/remoteT
|
|||
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {DefaultConfig} from 'vs/editor/common/config/defaultConfig';
|
||||
import {IActionDescriptor, ICodeEditorWidgetCreationOptions, IDiffEditorOptions, IModel, IModelChangedEvent, EventType} from 'vs/editor/common/editorCommon';
|
||||
import {IMode} from 'vs/editor/common/modes';
|
||||
import {HoverProvider, IMode} from 'vs/editor/common/modes';
|
||||
import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
|
||||
import {ILanguage} from 'vs/editor/common/modes/monarch/monarchTypes';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
|
@ -38,6 +38,11 @@ import {SimpleEditorService, StandaloneKeybindingService} from 'vs/editor/browse
|
|||
import {IEditorContextViewService, IEditorOverrideServices, ensureDynamicPlatformServices, ensureStaticPlatformServices, getOrCreateStaticServices} from 'vs/editor/browser/standalone/standaloneServices';
|
||||
import {CodeEditorWidget} from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import {DiffEditorWidget} from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import {EditorModelManager} from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import {SimpleWorkerClient} from 'vs/base/common/worker/simpleWorker';
|
||||
import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory';
|
||||
import {StandaloneWorker} from 'vs/editor/browser/standalone/standaloneWorker';
|
||||
|
||||
// Set defaults for standalone editor
|
||||
DefaultConfig.editor.wrappingIndent = 'none';
|
||||
|
@ -101,7 +106,7 @@ class StandaloneEditor extends CodeEditorWidget {
|
|||
if (model) {
|
||||
let e: IModelChangedEvent = {
|
||||
oldModelUrl: null,
|
||||
newModelUrl: model.getAssociatedResource().toString()
|
||||
newModelUrl: model.uri.toString()
|
||||
};
|
||||
this.emit(EventType.ModelChanged, e);
|
||||
}
|
||||
|
@ -457,6 +462,87 @@ export function createCustomMode(language:ILanguage): TPromise<IMode> {
|
|||
return modeService.getOrCreateMode(modeId);
|
||||
}
|
||||
|
||||
export function registerTokensProvider(languageId:string, support:modes.ITokenizationSupport2): IDisposable {
|
||||
startup.initStaticServicesIfNecessary();
|
||||
let staticPlatformServices = ensureStaticPlatformServices(null);
|
||||
|
||||
return staticPlatformServices.modeService.registerTokenizationSupport2(languageId, support);
|
||||
}
|
||||
|
||||
export function registerHoverProvider(languageId:string, support:HoverProvider): IDisposable {
|
||||
return modes.HoverProviderRegistry.register(languageId, support);
|
||||
}
|
||||
|
||||
interface IMonacoWebWorkerState<T> {
|
||||
myProxy:StandaloneWorker;
|
||||
foreignProxy:T;
|
||||
modelMananger: EditorModelManager;
|
||||
}
|
||||
|
||||
export class MonacoWebWorker<T> {
|
||||
|
||||
private _loaded: TPromise<IMonacoWebWorkerState<T>>;
|
||||
private _client: SimpleWorkerClient<StandaloneWorker>;
|
||||
|
||||
constructor(modelService: IModelService, opts:IWebWorkerOptions) {
|
||||
this._client = new SimpleWorkerClient<StandaloneWorker>(new DefaultWorkerFactory(), 'vs/editor/browser/standalone/standaloneWorker', null);
|
||||
|
||||
this._loaded = this._client.getProxyObject().then((proxy) => {
|
||||
|
||||
let proxyMethodRequest = (method:string, args:any[]): TPromise<any> => {
|
||||
return proxy.fmr(method, args);
|
||||
};
|
||||
|
||||
let createProxyMethod = (method:string, proxyMethodRequest:(method:string, args:any[])=>TPromise<any>): Function => {
|
||||
return function () {
|
||||
let args = Array.prototype.slice.call(arguments, 0);
|
||||
return proxyMethodRequest(method, args);
|
||||
};
|
||||
};
|
||||
|
||||
const manager = new EditorModelManager(proxy, modelService, true);
|
||||
|
||||
return proxy.loadModule(opts.moduleId).then((foreignMethods): IMonacoWebWorkerState<T> => {
|
||||
|
||||
let foreignProxy = <T><any>{};
|
||||
for (let i = 0; i < foreignMethods.length; i++) {
|
||||
foreignProxy[foreignMethods[i]] = createProxyMethod(foreignMethods[i], proxyMethodRequest);
|
||||
}
|
||||
|
||||
return {
|
||||
myProxy: proxy,
|
||||
foreignProxy: foreignProxy,
|
||||
modelMananger: manager
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
console.log('TODO: I should dispose now');
|
||||
}
|
||||
|
||||
public getProxy(): TPromise<T> {
|
||||
return this._loaded.then(data => data.foreignProxy);
|
||||
}
|
||||
|
||||
public withSyncedResources(resources: URI[]): TPromise<void> {
|
||||
return this._loaded.then(data => data.modelMananger.withSyncedResources(resources));
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWebWorkerOptions {
|
||||
moduleId: string;
|
||||
}
|
||||
|
||||
export function createWebWorker<T>(opts:IWebWorkerOptions): MonacoWebWorker<T> {
|
||||
startup.initStaticServicesIfNecessary();
|
||||
let staticPlatformServices = ensureStaticPlatformServices(null);
|
||||
let modelService = staticPlatformServices.modelService;
|
||||
|
||||
return new MonacoWebWorker(modelService, opts);
|
||||
}
|
||||
|
||||
export function registerMonarchStandaloneLanguage(language:ILanguageExtensionPoint, defModule:string): void {
|
||||
ModesRegistry.registerLanguage(language);
|
||||
|
||||
|
@ -501,6 +587,10 @@ export function registerStandaloneLanguage(language:ILanguageExtensionPoint, def
|
|||
});
|
||||
}
|
||||
|
||||
export function registerStandaloneLanguage2(language:ILanguageExtensionPoint, defModule:string): void {
|
||||
ModesRegistry.registerLanguage(language);
|
||||
}
|
||||
|
||||
export function registerStandaloneSchema(uri:string, schema:IJSONSchema) {
|
||||
let schemaRegistry = <IJSONContributionRegistry>Registry.as(Extensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(uri, schema);
|
||||
|
|
|
@ -7,12 +7,15 @@
|
|||
import 'vs/editor/standalone-languages/all';
|
||||
import './standaloneSchemas';
|
||||
import 'vs/css!./media/standalone-tokens';
|
||||
import {Emitter} from 'vs/base/common/event';
|
||||
import {IJSONSchema} from 'vs/base/common/jsonSchema';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import {ClassNames, ContentWidgetPositionPreference, OverlayWidgetPositionPreference} from 'vs/editor/browser/editorBrowser';
|
||||
import {Colorizer} from 'vs/editor/browser/standalone/colorizer';
|
||||
import * as standaloneCodeEditor from 'vs/editor/browser/standalone/standaloneCodeEditor';
|
||||
import {ILanguageDef} from 'vs/editor/standalone-languages/types';
|
||||
// import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
|
||||
import {ExtensionsRegistry} from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
|
||||
var global:any = self;
|
||||
if (!global.Monaco) {
|
||||
|
@ -77,3 +80,34 @@ if (!Monaco.Languages) {
|
|||
Monaco.Languages = {};
|
||||
}
|
||||
Monaco.Languages.register = standaloneCodeEditor.registerStandaloneLanguage;
|
||||
Monaco.Languages.register2 = standaloneCodeEditor.registerStandaloneLanguage2;
|
||||
Monaco.Languages.onLanguage = (languageId:string, callback:()=>void) => {
|
||||
let isDisposed = false;
|
||||
ExtensionsRegistry.registerOneTimeActivationEventListener('onLanguage:' + languageId, () => {
|
||||
if (!isDisposed) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
return {
|
||||
dispose: () => { isDisposed = true; }
|
||||
};
|
||||
};
|
||||
Monaco.createWebWorker = standaloneCodeEditor.createWebWorker;
|
||||
Monaco.Languages.registerTokensProvider = standaloneCodeEditor.registerTokensProvider;
|
||||
Monaco.Languages.registerHoverProvider = standaloneCodeEditor.registerHoverProvider;
|
||||
Monaco.Emitter = Emitter;
|
||||
// let handlePlugin = (plugin) => {
|
||||
// if (Array.isArray(plugin.languages)) {
|
||||
// ModesRegistry.registerLanguages(plugin.languages);
|
||||
// }
|
||||
// if (plugin.activate) {
|
||||
// try {
|
||||
// plugin.activate();
|
||||
// } catch(err) {
|
||||
// console.error(err);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// let MonacoPlugins = this.MonacoPlugins || [];
|
||||
// MonacoPlugins.forEach(handlePlugin);
|
||||
// this.MonacoPlugins = { push: handlePlugin };
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue