diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 230bdfe8a82..aadcda2705f 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -238,6 +238,14 @@ if (require.main === module) { cp.exec('git config core.autocrlf', (err, out) => { const skipEOL = out.trim() === 'true'; + if (process.argv.length > 2) { + return hygiene(process.argv.slice(2), { skipEOL: skipEOL }).on('error', err => { + console.error(); + console.error(err); + process.exit(1); + }); + } + cp.exec('git diff --cached --name-only', { maxBuffer: 2000 * 1024 }, (err, out) => { if (err) { console.error(); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index e6953c841ae..6afb6300c8b 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -40,7 +40,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.11.9' }, + { name: 'ms-vscode.node-debug', version: '1.11.10' }, { name: 'ms-vscode.node-debug2', version: '1.11.8' } ]; diff --git a/build/lib/bundle.js b/build/lib/bundle.js index bcf5340e0b0..78375d51eb6 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -1,452 +1,451 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var fs = require("fs"); -var path = require("path"); -var vm = require("vm"); -/** - * Bundle `entryPoints` given config `config`. - */ -function bundle(entryPoints, config, callback) { - var entryPointsMap = {}; - entryPoints.forEach(function (module) { - entryPointsMap[module.name] = module; - }); - var allMentionedModulesMap = {}; - entryPoints.forEach(function (module) { - allMentionedModulesMap[module.name] = true; - (module.include || []).forEach(function (includedModule) { - allMentionedModulesMap[includedModule] = true; - }); - (module.exclude || []).forEach(function (excludedModule) { - allMentionedModulesMap[excludedModule] = true; - }); - }); - var code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); - var r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); - var loaderModule = { exports: {} }; - r.call({}, require, loaderModule, loaderModule.exports); - var loader = loaderModule.exports; - config.isBuild = true; - loader.config(config); - loader(['require'], function (localRequire) { - var resolvePath = function (path) { - var r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; - } - return r; - }; - for (var moduleId in entryPointsMap) { - var entryPoint = entryPointsMap[moduleId]; - if (entryPoint.append) { - entryPoint.append = entryPoint.append.map(resolvePath); - } - if (entryPoint.prepend) { - entryPoint.prepend = entryPoint.prepend.map(resolvePath); - } - } - }); - loader(Object.keys(allMentionedModulesMap), function () { - var modules = loader.getBuildInfo(); - var partialResult = emitEntryPoints(modules, entryPointsMap); - var cssInlinedResources = loader('vs/css').getInlinedResources(); - callback(null, { - files: partialResult.files, - cssInlinedResources: cssInlinedResources, - bundleData: partialResult.bundleData - }); - }, function (err) { return callback(err, null); }); -} -exports.bundle = bundle; -function emitEntryPoints(modules, entryPoints) { - var modulesMap = {}; - modules.forEach(function (m) { - modulesMap[m.id] = m; - }); - var modulesGraph = {}; - modules.forEach(function (m) { - modulesGraph[m.id] = m.dependencies; - }); - var sortedModules = topologicalSort(modulesGraph); - var result = []; - var usedPlugins = {}; - var bundleData = { - graph: modulesGraph, - bundles: {} - }; - Object.keys(entryPoints).forEach(function (moduleToBundle) { - var info = entryPoints[moduleToBundle]; - var rootNodes = [moduleToBundle].concat(info.include || []); - var allDependencies = visit(rootNodes, modulesGraph); - var excludes = ['require', 'exports', 'module'].concat(info.exclude || []); - excludes.forEach(function (excludeRoot) { - var allExcludes = visit([excludeRoot], modulesGraph); - Object.keys(allExcludes).forEach(function (exclude) { - delete allDependencies[exclude]; - }); - }); - var includedModules = sortedModules.filter(function (module) { - return allDependencies[module]; - }); - bundleData.bundles[moduleToBundle] = includedModules; - var res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend, info.append, info.dest); - result = result.concat(res.files); - for (var pluginName in res.usedPlugins) { - usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; - } - }); - Object.keys(usedPlugins).forEach(function (pluginName) { - var plugin = usedPlugins[pluginName]; - if (typeof plugin.finishBuild === 'function') { - var write = function (filename, contents) { - result.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.finishBuild(write); - } - }); - return { - files: extractStrings(removeDuplicateTSBoilerplate(result)), - bundleData: bundleData - }; -} -function extractStrings(destFiles) { - var parseDefineCall = function (moduleMatch, depsMatch) { - var module = moduleMatch.replace(/^"|"$/g, ''); - var deps = depsMatch.split(','); - deps = deps.map(function (dep) { - dep = dep.trim(); - dep = dep.replace(/^"|"$/g, ''); - dep = dep.replace(/^'|'$/g, ''); - var prefix = null; - var _path = null; - var pieces = dep.split('!'); - if (pieces.length > 1) { - prefix = pieces[0] + '!'; - _path = pieces[1]; - } - else { - prefix = ''; - _path = pieces[0]; - } - if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { - var res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); - return prefix + res; - } - return prefix + _path; - }); - return { - module: module, - deps: deps - }; - }; - destFiles.forEach(function (destFile, index) { - if (!/\.js$/.test(destFile.dest)) { - return; - } - if (/\.nls\.js$/.test(destFile.dest)) { - return; - } - // Do one pass to record the usage counts for each module id - var useCounts = {}; - destFile.sources.forEach(function (source) { - var matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); - if (!matches) { - return; - } - var defineCall = parseDefineCall(matches[1], matches[2]); - useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; - defineCall.deps.forEach(function (dep) { - useCounts[dep] = (useCounts[dep] || 0) + 1; - }); - }); - var sortedByUseModules = Object.keys(useCounts); - sortedByUseModules.sort(function (a, b) { - return useCounts[b] - useCounts[a]; - }); - var replacementMap = {}; - sortedByUseModules.forEach(function (module, index) { - replacementMap[module] = index; - }); - destFile.sources.forEach(function (source) { - source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, function (_, moduleMatch, depsMatch) { - var defineCall = parseDefineCall(moduleMatch, depsMatch); - return "define(__m[" + replacementMap[defineCall.module] + "/*" + defineCall.module + "*/], __M([" + defineCall.deps.map(function (dep) { return replacementMap[dep] + '/*' + dep + '*/'; }).join(',') + "])"; - }); - }); - destFile.sources.unshift({ - path: null, - contents: [ - '(function() {', - "var __m = " + JSON.stringify(sortedByUseModules) + ";", - "var __M = function(deps) {", - " var result = [];", - " for (var i = 0, len = deps.length; i < len; i++) {", - " result[i] = __m[deps[i]];", - " }", - " return result;", - "};" - ].join('\n') - }); - destFile.sources.push({ - path: null, - contents: '}).call(this);' - }); - }); - return destFiles; -} -function removeDuplicateTSBoilerplate(destFiles) { - // Taken from typescript compiler => emitFiles - var BOILERPLATE = [ - { start: /^var __extends/, end: /^};$/ }, - { start: /^var __assign/, end: /^};$/ }, - { start: /^var __decorate/, end: /^};$/ }, - { start: /^var __metadata/, end: /^};$/ }, - { start: /^var __param/, end: /^};$/ }, - { start: /^var __awaiter/, end: /^};$/ }, - ]; - destFiles.forEach(function (destFile) { - var SEEN_BOILERPLATE = []; - destFile.sources.forEach(function (source) { - var lines = source.contents.split(/\r\n|\n|\r/); - var newLines = []; - var IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (IS_REMOVING_BOILERPLATE) { - newLines.push(''); - if (END_BOILERPLATE.test(line)) { - IS_REMOVING_BOILERPLATE = false; - } - } - else { - for (var j = 0; j < BOILERPLATE.length; j++) { - var boilerplate = BOILERPLATE[j]; - if (boilerplate.start.test(line)) { - if (SEEN_BOILERPLATE[j]) { - IS_REMOVING_BOILERPLATE = true; - END_BOILERPLATE = boilerplate.end; - } - else { - SEEN_BOILERPLATE[j] = true; - } - } - } - if (IS_REMOVING_BOILERPLATE) { - newLines.push(''); - } - else { - newLines.push(line); - } - } - } - source.contents = newLines.join('\n'); - }); - }); - return destFiles; -} -function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) { - if (!dest) { - dest = entryPoint + '.js'; - } - var mainResult = { - sources: [], - dest: dest - }, results = [mainResult]; - var usedPlugins = {}; - var getLoaderPlugin = function (pluginName) { - if (!usedPlugins[pluginName]) { - usedPlugins[pluginName] = modulesMap[pluginName].exports; - } - return usedPlugins[pluginName]; - }; - includedModules.forEach(function (c) { - var bangIndex = c.indexOf('!'); - if (bangIndex >= 0) { - var pluginName = c.substr(0, bangIndex); - var plugin = getLoaderPlugin(pluginName); - mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); - return; - } - var module = modulesMap[c]; - if (module.path === 'empty:') { - return; - } - var contents = readFileAndRemoveBOM(module.path); - if (module.shim) { - mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } - else { - mainResult.sources.push(emitNamedModule(c, deps[c], module.defineLocation, module.path, contents)); - } - }); - Object.keys(usedPlugins).forEach(function (pluginName) { - var plugin = usedPlugins[pluginName]; - if (typeof plugin.writeFile === 'function') { - var req = (function () { - throw new Error('no-no!'); - }); - req.toUrl = function (something) { return something; }; - var write = function (filename, contents) { - results.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.writeFile(pluginName, entryPoint, req, write, {}); - } - }); - var toIFile = function (path) { - var contents = readFileAndRemoveBOM(path); - return { - path: path, - contents: contents - }; - }; - var toPrepend = (prepend || []).map(toIFile); - var toAppend = (append || []).map(toIFile); - mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); - return { - files: results, - usedPlugins: usedPlugins - }; -} -function readFileAndRemoveBOM(path) { - var BOM_CHAR_CODE = 65279; - var contents = fs.readFileSync(path, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === BOM_CHAR_CODE) { - contents = contents.substring(1); - } - return contents; -} -function emitPlugin(entryPoint, plugin, pluginName, moduleName) { - var result = ''; - if (typeof plugin.write === 'function') { - var write = (function (what) { - result += what; - }); - write.getEntryPoint = function () { - return entryPoint; - }; - write.asModule = function (moduleId, code) { - code = code.replace(/^define\(/, 'define("' + moduleId + '",'); - result += code; - }; - plugin.write(pluginName, moduleName, write); - } - return { - path: null, - contents: result - }; -} -function emitNamedModule(moduleId, myDeps, defineCallPosition, path, contents) { - // `defineCallPosition` is the position in code: |define() - var defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); - // `parensOffset` is the position in code: define|() - var parensOffset = contents.indexOf('(', defineCallOffset); - var insertStr = '"' + moduleId + '", '; - return { - path: path, - contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) - }; -} -function emitShimmedModule(moduleId, myDeps, factory, path, contents) { - var strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); - var strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; - return { - path: path, - contents: contents + '\n;\n' + strDefine - }; -} -/** - * Convert a position (line:col) to (offset) in string `str` - */ -function positionToOffset(str, desiredLine, desiredCol) { - if (desiredLine === 1) { - return desiredCol - 1; - } - var line = 1, lastNewLineOffset = -1; - do { - if (desiredLine === line) { - return lastNewLineOffset + 1 + desiredCol - 1; - } - lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); - line++; - } while (lastNewLineOffset >= 0); - return -1; -} -/** - * Return a set of reachable nodes in `graph` starting from `rootNodes` - */ -function visit(rootNodes, graph) { - var result = {}, queue = rootNodes; - rootNodes.forEach(function (node) { - result[node] = true; - }); - while (queue.length > 0) { - var el = queue.shift(); - var myEdges = graph[el] || []; - myEdges.forEach(function (toNode) { - if (!result[toNode]) { - result[toNode] = true; - queue.push(toNode); - } - }); - } - return result; -} -/** - * Perform a topological sort on `graph` - */ -function topologicalSort(graph) { - var allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {}; - Object.keys(graph).forEach(function (fromNode) { - allNodes[fromNode] = true; - outgoingEdgeCount[fromNode] = graph[fromNode].length; - graph[fromNode].forEach(function (toNode) { - allNodes[toNode] = true; - outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; - inverseEdges[toNode] = inverseEdges[toNode] || []; - inverseEdges[toNode].push(fromNode); - }); - }); - // https://en.wikipedia.org/wiki/Topological_sorting - var S = [], L = []; - Object.keys(allNodes).forEach(function (node) { - if (outgoingEdgeCount[node] === 0) { - delete outgoingEdgeCount[node]; - S.push(node); - } - }); - while (S.length > 0) { - // Ensure the exact same order all the time with the same inputs - S.sort(); - var n = S.shift(); - L.push(n); - var myInverseEdges = inverseEdges[n] || []; - myInverseEdges.forEach(function (m) { - outgoingEdgeCount[m]--; - if (outgoingEdgeCount[m] === 0) { - delete outgoingEdgeCount[m]; - S.push(m); - } - }); - } - if (Object.keys(outgoingEdgeCount).length > 0) { - throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); - } - return L; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var fs = require("fs"); +var path = require("path"); +var vm = require("vm"); +/** + * Bundle `entryPoints` given config `config`. + */ +function bundle(entryPoints, config, callback) { + var entryPointsMap = {}; + entryPoints.forEach(function (module) { + entryPointsMap[module.name] = module; + }); + var allMentionedModulesMap = {}; + entryPoints.forEach(function (module) { + allMentionedModulesMap[module.name] = true; + (module.include || []).forEach(function (includedModule) { + allMentionedModulesMap[includedModule] = true; + }); + (module.exclude || []).forEach(function (excludedModule) { + allMentionedModulesMap[excludedModule] = true; + }); + }); + var code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); + var r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); + var loaderModule = { exports: {} }; + r.call({}, require, loaderModule, loaderModule.exports); + var loader = loaderModule.exports; + config.isBuild = true; + loader.config(config); + loader(['require'], function (localRequire) { + var resolvePath = function (path) { + var r = localRequire.toUrl(path); + if (!/\.js/.test(r)) { + return r + '.js'; + } + return r; + }; + for (var moduleId in entryPointsMap) { + var entryPoint = entryPointsMap[moduleId]; + if (entryPoint.append) { + entryPoint.append = entryPoint.append.map(resolvePath); + } + if (entryPoint.prepend) { + entryPoint.prepend = entryPoint.prepend.map(resolvePath); + } + } + }); + loader(Object.keys(allMentionedModulesMap), function () { + var modules = loader.getBuildInfo(); + var partialResult = emitEntryPoints(modules, entryPointsMap); + var cssInlinedResources = loader('vs/css').getInlinedResources(); + callback(null, { + files: partialResult.files, + cssInlinedResources: cssInlinedResources, + bundleData: partialResult.bundleData + }); + }, function (err) { return callback(err, null); }); +} +exports.bundle = bundle; +function emitEntryPoints(modules, entryPoints) { + var modulesMap = {}; + modules.forEach(function (m) { + modulesMap[m.id] = m; + }); + var modulesGraph = {}; + modules.forEach(function (m) { + modulesGraph[m.id] = m.dependencies; + }); + var sortedModules = topologicalSort(modulesGraph); + var result = []; + var usedPlugins = {}; + var bundleData = { + graph: modulesGraph, + bundles: {} + }; + Object.keys(entryPoints).forEach(function (moduleToBundle) { + var info = entryPoints[moduleToBundle]; + var rootNodes = [moduleToBundle].concat(info.include || []); + var allDependencies = visit(rootNodes, modulesGraph); + var excludes = ['require', 'exports', 'module'].concat(info.exclude || []); + excludes.forEach(function (excludeRoot) { + var allExcludes = visit([excludeRoot], modulesGraph); + Object.keys(allExcludes).forEach(function (exclude) { + delete allDependencies[exclude]; + }); + }); + var includedModules = sortedModules.filter(function (module) { + return allDependencies[module]; + }); + bundleData.bundles[moduleToBundle] = includedModules; + var res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend, info.append, info.dest); + result = result.concat(res.files); + for (var pluginName in res.usedPlugins) { + usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; + } + }); + Object.keys(usedPlugins).forEach(function (pluginName) { + var plugin = usedPlugins[pluginName]; + if (typeof plugin.finishBuild === 'function') { + var write = function (filename, contents) { + result.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.finishBuild(write); + } + }); + return { + files: extractStrings(removeDuplicateTSBoilerplate(result)), + bundleData: bundleData + }; +} +function extractStrings(destFiles) { + var parseDefineCall = function (moduleMatch, depsMatch) { + var module = moduleMatch.replace(/^"|"$/g, ''); + var deps = depsMatch.split(','); + deps = deps.map(function (dep) { + dep = dep.trim(); + dep = dep.replace(/^"|"$/g, ''); + dep = dep.replace(/^'|'$/g, ''); + var prefix = null; + var _path = null; + var pieces = dep.split('!'); + if (pieces.length > 1) { + prefix = pieces[0] + '!'; + _path = pieces[1]; + } + else { + prefix = ''; + _path = pieces[0]; + } + if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { + var res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); + return prefix + res; + } + return prefix + _path; + }); + return { + module: module, + deps: deps + }; + }; + destFiles.forEach(function (destFile, index) { + if (!/\.js$/.test(destFile.dest)) { + return; + } + if (/\.nls\.js$/.test(destFile.dest)) { + return; + } + // Do one pass to record the usage counts for each module id + var useCounts = {}; + destFile.sources.forEach(function (source) { + var matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); + if (!matches) { + return; + } + var defineCall = parseDefineCall(matches[1], matches[2]); + useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; + defineCall.deps.forEach(function (dep) { + useCounts[dep] = (useCounts[dep] || 0) + 1; + }); + }); + var sortedByUseModules = Object.keys(useCounts); + sortedByUseModules.sort(function (a, b) { + return useCounts[b] - useCounts[a]; + }); + var replacementMap = {}; + sortedByUseModules.forEach(function (module, index) { + replacementMap[module] = index; + }); + destFile.sources.forEach(function (source) { + source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, function (_, moduleMatch, depsMatch) { + var defineCall = parseDefineCall(moduleMatch, depsMatch); + return "define(__m[" + replacementMap[defineCall.module] + "/*" + defineCall.module + "*/], __M([" + defineCall.deps.map(function (dep) { return replacementMap[dep] + '/*' + dep + '*/'; }).join(',') + "])"; + }); + }); + destFile.sources.unshift({ + path: null, + contents: [ + '(function() {', + "var __m = " + JSON.stringify(sortedByUseModules) + ";", + "var __M = function(deps) {", + " var result = [];", + " for (var i = 0, len = deps.length; i < len; i++) {", + " result[i] = __m[deps[i]];", + " }", + " return result;", + "};" + ].join('\n') + }); + destFile.sources.push({ + path: null, + contents: '}).call(this);' + }); + }); + return destFiles; +} +function removeDuplicateTSBoilerplate(destFiles) { + // Taken from typescript compiler => emitFiles + var BOILERPLATE = [ + { start: /^var __extends/, end: /^};$/ }, + { start: /^var __assign/, end: /^};$/ }, + { start: /^var __decorate/, end: /^};$/ }, + { start: /^var __metadata/, end: /^};$/ }, + { start: /^var __param/, end: /^};$/ }, + { start: /^var __awaiter/, end: /^};$/ }, + ]; + destFiles.forEach(function (destFile) { + var SEEN_BOILERPLATE = []; + destFile.sources.forEach(function (source) { + var lines = source.contents.split(/\r\n|\n|\r/); + var newLines = []; + var IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + if (END_BOILERPLATE.test(line)) { + IS_REMOVING_BOILERPLATE = false; + } + } + else { + for (var j = 0; j < BOILERPLATE.length; j++) { + var boilerplate = BOILERPLATE[j]; + if (boilerplate.start.test(line)) { + if (SEEN_BOILERPLATE[j]) { + IS_REMOVING_BOILERPLATE = true; + END_BOILERPLATE = boilerplate.end; + } + else { + SEEN_BOILERPLATE[j] = true; + } + } + } + if (IS_REMOVING_BOILERPLATE) { + newLines.push(''); + } + else { + newLines.push(line); + } + } + } + source.contents = newLines.join('\n'); + }); + }); + return destFiles; +} +function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) { + if (!dest) { + dest = entryPoint + '.js'; + } + var mainResult = { + sources: [], + dest: dest + }, results = [mainResult]; + var usedPlugins = {}; + var getLoaderPlugin = function (pluginName) { + if (!usedPlugins[pluginName]) { + usedPlugins[pluginName] = modulesMap[pluginName].exports; + } + return usedPlugins[pluginName]; + }; + includedModules.forEach(function (c) { + var bangIndex = c.indexOf('!'); + if (bangIndex >= 0) { + var pluginName = c.substr(0, bangIndex); + var plugin = getLoaderPlugin(pluginName); + mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); + return; + } + var module = modulesMap[c]; + if (module.path === 'empty:') { + return; + } + var contents = readFileAndRemoveBOM(module.path); + if (module.shim) { + mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); + } + else { + mainResult.sources.push(emitNamedModule(c, deps[c], module.defineLocation, module.path, contents)); + } + }); + Object.keys(usedPlugins).forEach(function (pluginName) { + var plugin = usedPlugins[pluginName]; + if (typeof plugin.writeFile === 'function') { + var req = (function () { + throw new Error('no-no!'); + }); + req.toUrl = function (something) { return something; }; + var write = function (filename, contents) { + results.push({ + dest: filename, + sources: [{ + path: null, + contents: contents + }] + }); + }; + plugin.writeFile(pluginName, entryPoint, req, write, {}); + } + }); + var toIFile = function (path) { + var contents = readFileAndRemoveBOM(path); + return { + path: path, + contents: contents + }; + }; + var toPrepend = (prepend || []).map(toIFile); + var toAppend = (append || []).map(toIFile); + mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); + return { + files: results, + usedPlugins: usedPlugins + }; +} +function readFileAndRemoveBOM(path) { + var BOM_CHAR_CODE = 65279; + var contents = fs.readFileSync(path, 'utf8'); + // Remove BOM + if (contents.charCodeAt(0) === BOM_CHAR_CODE) { + contents = contents.substring(1); + } + return contents; +} +function emitPlugin(entryPoint, plugin, pluginName, moduleName) { + var result = ''; + if (typeof plugin.write === 'function') { + var write = (function (what) { + result += what; + }); + write.getEntryPoint = function () { + return entryPoint; + }; + write.asModule = function (moduleId, code) { + code = code.replace(/^define\(/, 'define("' + moduleId + '",'); + result += code; + }; + plugin.write(pluginName, moduleName, write); + } + return { + path: null, + contents: result + }; +} +function emitNamedModule(moduleId, myDeps, defineCallPosition, path, contents) { + // `defineCallPosition` is the position in code: |define() + var defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); + // `parensOffset` is the position in code: define|() + var parensOffset = contents.indexOf('(', defineCallOffset); + var insertStr = '"' + moduleId + '", '; + return { + path: path, + contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) + }; +} +function emitShimmedModule(moduleId, myDeps, factory, path, contents) { + var strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); + var strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; + return { + path: path, + contents: contents + '\n;\n' + strDefine + }; +} +/** + * Convert a position (line:col) to (offset) in string `str` + */ +function positionToOffset(str, desiredLine, desiredCol) { + if (desiredLine === 1) { + return desiredCol - 1; + } + var line = 1, lastNewLineOffset = -1; + do { + if (desiredLine === line) { + return lastNewLineOffset + 1 + desiredCol - 1; + } + lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); + line++; + } while (lastNewLineOffset >= 0); + return -1; +} +/** + * Return a set of reachable nodes in `graph` starting from `rootNodes` + */ +function visit(rootNodes, graph) { + var result = {}, queue = rootNodes; + rootNodes.forEach(function (node) { + result[node] = true; + }); + while (queue.length > 0) { + var el = queue.shift(); + var myEdges = graph[el] || []; + myEdges.forEach(function (toNode) { + if (!result[toNode]) { + result[toNode] = true; + queue.push(toNode); + } + }); + } + return result; +} +/** + * Perform a topological sort on `graph` + */ +function topologicalSort(graph) { + var allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {}; + Object.keys(graph).forEach(function (fromNode) { + allNodes[fromNode] = true; + outgoingEdgeCount[fromNode] = graph[fromNode].length; + graph[fromNode].forEach(function (toNode) { + allNodes[toNode] = true; + outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; + inverseEdges[toNode] = inverseEdges[toNode] || []; + inverseEdges[toNode].push(fromNode); + }); + }); + // https://en.wikipedia.org/wiki/Topological_sorting + var S = [], L = []; + Object.keys(allNodes).forEach(function (node) { + if (outgoingEdgeCount[node] === 0) { + delete outgoingEdgeCount[node]; + S.push(node); + } + }); + while (S.length > 0) { + // Ensure the exact same order all the time with the same inputs + S.sort(); + var n = S.shift(); + L.push(n); + var myInverseEdges = inverseEdges[n] || []; + myInverseEdges.forEach(function (m) { + outgoingEdgeCount[m]--; + if (outgoingEdgeCount[m] === 0) { + delete outgoingEdgeCount[m]; + S.push(m); + } + }); + } + if (Object.keys(outgoingEdgeCount).length > 0) { + throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); + } + return L; +} diff --git a/build/lib/compilation.js b/build/lib/compilation.js index d9691ebc1d2..66078676a5a 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -1,171 +1,170 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -var gulp = require("gulp"); -var tsb = require("gulp-tsb"); -var es = require("event-stream"); -var watch = require('./watch'); -var nls = require("./nls"); -var util = require("./util"); -var reporter_1 = require("./reporter"); -var path = require("path"); -var bom = require("gulp-bom"); -var sourcemaps = require("gulp-sourcemaps"); -var _ = require("underscore"); -var monacodts = require("../monaco/api"); -var fs = require("fs"); -var reporter = reporter_1.createReporter(); -var rootDir = path.join(__dirname, '../../src'); -var options = require('../../src/tsconfig.json').compilerOptions; -options.verbose = false; -options.sourceMap = true; -options.rootDir = rootDir; -options.sourceRoot = util.toFileUri(rootDir); -function createCompile(build, emitError) { - var opts = _.clone(options); - opts.inlineSources = !!build; - opts.noFilesystemLookup = true; - var ts = tsb.create(opts, null, null, function (err) { return reporter(err.toString()); }); - return function (token) { - var utf8Filter = util.filter(function (data) { return /(\/|\\)test(\/|\\).*utf8/.test(data.path); }); - var tsFilter = util.filter(function (data) { return /\.ts$/.test(data.path); }); - var noDeclarationsFilter = util.filter(function (data) { return !(/\.d\.ts$/.test(data.path)); }); - var input = es.through(); - var output = input - .pipe(utf8Filter) - .pipe(bom()) - .pipe(utf8Filter.restore) - .pipe(tsFilter) - .pipe(util.loadSourcemaps()) - .pipe(ts(token)) - .pipe(build ? reloadTypeScriptNodeModule() : es.through()) - .pipe(noDeclarationsFilter) - .pipe(build ? nls() : es.through()) - .pipe(noDeclarationsFilter.restore) - .pipe(sourcemaps.write('.', { - addComment: false, - includeContent: !!build, - sourceRoot: options.sourceRoot - })) - .pipe(tsFilter.restore) - .pipe(reporter.end(emitError)); - return es.duplex(input, output); - }; -} -function compileTask(out, build) { - return function () { - var compile = createCompile(build, true); - var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'), gulp.src('node_modules/@types/**/index.d.ts')); - return src - .pipe(compile()) - .pipe(gulp.dest(out)) - .pipe(monacodtsTask(out, false)); - }; -} -exports.compileTask = compileTask; -function watchTask(out, build) { - return function () { - var compile = createCompile(build); - var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'), gulp.src('node_modules/@types/**/index.d.ts')); - var watchSrc = watch('src/**', { base: 'src' }); - return watchSrc - .pipe(util.incremental(compile, src, true)) - .pipe(gulp.dest(out)) - .pipe(monacodtsTask(out, true)); - }; -} -exports.watchTask = watchTask; -function reloadTypeScriptNodeModule() { - var util = require('gulp-util'); - function log(message) { - var rest = []; - for (var _i = 1; _i < arguments.length; _i++) { - rest[_i - 1] = arguments[_i]; - } - util.log.apply(util, [util.colors.cyan('[memory watch dog]'), message].concat(rest)); - } - function heapUsed() { - return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB'; - } - return es.through(function (data) { - this.emit('data', data); - }, function () { - log('memory usage after compilation finished: ' + heapUsed()); - // It appears we are running into some variant of - // https://bugs.chromium.org/p/v8/issues/detail?id=2073 - // - // Even though all references are dropped, some - // optimized methods in the TS compiler end up holding references - // to the entire TypeScript language host (>600MB) - // - // The idea is to force v8 to drop references to these - // optimized methods, by "reloading" the typescript node module - log('Reloading typescript node module...'); - var resolvedName = require.resolve('typescript'); - var originalModule = require.cache[resolvedName]; - delete require.cache[resolvedName]; - var newExports = require('typescript'); - require.cache[resolvedName] = originalModule; - for (var prop in newExports) { - if (newExports.hasOwnProperty(prop)) { - originalModule.exports[prop] = newExports[prop]; - } - } - log('typescript node module reloaded.'); - this.emit('end'); - }); -} -function monacodtsTask(out, isWatch) { - var neededFiles = {}; - monacodts.getFilesToWatch(out).forEach(function (filePath) { - filePath = path.normalize(filePath); - neededFiles[filePath] = true; - }); - var inputFiles = {}; - for (var filePath in neededFiles) { - if (/\bsrc(\/|\\)vs\b/.test(filePath)) { - // This file is needed from source => simply read it now - inputFiles[filePath] = fs.readFileSync(filePath).toString(); - } - } - var setInputFile = function (filePath, contents) { - if (inputFiles[filePath] === contents) { - // no change - return; - } - inputFiles[filePath] = contents; - var neededInputFilesCount = Object.keys(neededFiles).length; - var availableInputFilesCount = Object.keys(inputFiles).length; - if (neededInputFilesCount === availableInputFilesCount) { - run(); - } - }; - var run = function () { - var result = monacodts.run(out, inputFiles); - if (!result.isTheSame) { - if (isWatch) { - fs.writeFileSync(result.filePath, result.content); - } - else { - resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); - } - } - }; - var resultStream; - if (isWatch) { - watch('build/monaco/*').pipe(es.through(function () { - run(); - })); - } - resultStream = es.through(function (data) { - var filePath = path.normalize(data.path); - if (neededFiles[filePath]) { - setInputFile(filePath, data.contents.toString()); - } - this.emit('data', data); - }); - return resultStream; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var gulp = require("gulp"); +var tsb = require("gulp-tsb"); +var es = require("event-stream"); +var watch = require('./watch'); +var nls = require("./nls"); +var util = require("./util"); +var reporter_1 = require("./reporter"); +var path = require("path"); +var bom = require("gulp-bom"); +var sourcemaps = require("gulp-sourcemaps"); +var _ = require("underscore"); +var monacodts = require("../monaco/api"); +var fs = require("fs"); +var reporter = reporter_1.createReporter(); +var rootDir = path.join(__dirname, '../../src'); +var options = require('../../src/tsconfig.json').compilerOptions; +options.verbose = false; +options.sourceMap = true; +options.rootDir = rootDir; +options.sourceRoot = util.toFileUri(rootDir); +function createCompile(build, emitError) { + var opts = _.clone(options); + opts.inlineSources = !!build; + opts.noFilesystemLookup = true; + var ts = tsb.create(opts, null, null, function (err) { return reporter(err.toString()); }); + return function (token) { + var utf8Filter = util.filter(function (data) { return /(\/|\\)test(\/|\\).*utf8/.test(data.path); }); + var tsFilter = util.filter(function (data) { return /\.ts$/.test(data.path); }); + var noDeclarationsFilter = util.filter(function (data) { return !(/\.d\.ts$/.test(data.path)); }); + var input = es.through(); + var output = input + .pipe(utf8Filter) + .pipe(bom()) + .pipe(utf8Filter.restore) + .pipe(tsFilter) + .pipe(util.loadSourcemaps()) + .pipe(ts(token)) + .pipe(build ? reloadTypeScriptNodeModule() : es.through()) + .pipe(noDeclarationsFilter) + .pipe(build ? nls() : es.through()) + .pipe(noDeclarationsFilter.restore) + .pipe(sourcemaps.write('.', { + addComment: false, + includeContent: !!build, + sourceRoot: options.sourceRoot + })) + .pipe(tsFilter.restore) + .pipe(reporter.end(emitError)); + return es.duplex(input, output); + }; +} +function compileTask(out, build) { + return function () { + var compile = createCompile(build, true); + var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'), gulp.src('node_modules/@types/**/index.d.ts')); + return src + .pipe(compile()) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, false)); + }; +} +exports.compileTask = compileTask; +function watchTask(out, build) { + return function () { + var compile = createCompile(build); + var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts'), gulp.src('node_modules/@types/**/index.d.ts')); + var watchSrc = watch('src/**', { base: 'src' }); + return watchSrc + .pipe(util.incremental(compile, src, true)) + .pipe(gulp.dest(out)) + .pipe(monacodtsTask(out, true)); + }; +} +exports.watchTask = watchTask; +function reloadTypeScriptNodeModule() { + var util = require('gulp-util'); + function log(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log.apply(util, [util.colors.cyan('[memory watch dog]'), message].concat(rest)); + } + function heapUsed() { + return (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB'; + } + return es.through(function (data) { + this.emit('data', data); + }, function () { + log('memory usage after compilation finished: ' + heapUsed()); + // It appears we are running into some variant of + // https://bugs.chromium.org/p/v8/issues/detail?id=2073 + // + // Even though all references are dropped, some + // optimized methods in the TS compiler end up holding references + // to the entire TypeScript language host (>600MB) + // + // The idea is to force v8 to drop references to these + // optimized methods, by "reloading" the typescript node module + log('Reloading typescript node module...'); + var resolvedName = require.resolve('typescript'); + var originalModule = require.cache[resolvedName]; + delete require.cache[resolvedName]; + var newExports = require('typescript'); + require.cache[resolvedName] = originalModule; + for (var prop in newExports) { + if (newExports.hasOwnProperty(prop)) { + originalModule.exports[prop] = newExports[prop]; + } + } + log('typescript node module reloaded.'); + this.emit('end'); + }); +} +function monacodtsTask(out, isWatch) { + var neededFiles = {}; + monacodts.getFilesToWatch(out).forEach(function (filePath) { + filePath = path.normalize(filePath); + neededFiles[filePath] = true; + }); + var inputFiles = {}; + for (var filePath in neededFiles) { + if (/\bsrc(\/|\\)vs\b/.test(filePath)) { + // This file is needed from source => simply read it now + inputFiles[filePath] = fs.readFileSync(filePath).toString(); + } + } + var setInputFile = function (filePath, contents) { + if (inputFiles[filePath] === contents) { + // no change + return; + } + inputFiles[filePath] = contents; + var neededInputFilesCount = Object.keys(neededFiles).length; + var availableInputFilesCount = Object.keys(inputFiles).length; + if (neededInputFilesCount === availableInputFilesCount) { + run(); + } + }; + var run = function () { + var result = monacodts.run(out, inputFiles); + if (!result.isTheSame) { + if (isWatch) { + fs.writeFileSync(result.filePath, result.content); + } + else { + resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); + } + } + }; + var resultStream; + if (isWatch) { + watch('build/monaco/*').pipe(es.through(function () { + run(); + })); + } + resultStream = es.through(function (data) { + var filePath = path.normalize(data.path); + if (neededFiles[filePath]) { + setInputFile(filePath, data.contents.toString()); + } + this.emit('data', data); + }); + return resultStream; +} diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 92733a7fe11..f2da7d5fd70 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -1,96 +1,95 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var event_stream_1 = require("event-stream"); -var assign = require("object-assign"); -var remote = require("gulp-remote-src"); -var flatmap = require('gulp-flatmap'); -var vzip = require('gulp-vinyl-zip'); -var filter = require('gulp-filter'); -var rename = require('gulp-rename'); -var util = require('gulp-util'); -var buffer = require('gulp-buffer'); -var json = require('gulp-json-editor'); -function error(err) { - var result = event_stream_1.through(); - setTimeout(function () { return result.emit('error', err); }); - return result; -} -var baseHeaders = { - 'X-Market-Client-Id': 'VSCode Build', - 'User-Agent': 'VSCode Build', -}; -function src(extensionName, version) { - var filterType = 7; - var value = extensionName; - var criterium = { filterType: filterType, value: value }; - var criteria = [criterium]; - var pageNumber = 1; - var pageSize = 1; - var sortBy = 0; - var sortOrder = 0; - var flags = 0x1 | 0x2 | 0x80; - var assetTypes = ['Microsoft.VisualStudio.Services.VSIXPackage']; - var filters = [{ criteria: criteria, pageNumber: pageNumber, pageSize: pageSize, sortBy: sortBy, sortOrder: sortOrder }]; - var body = JSON.stringify({ filters: filters, assetTypes: assetTypes, flags: flags }); - var headers = assign({}, baseHeaders, { - 'Content-Type': 'application/json', - 'Accept': 'application/json;api-version=3.0-preview.1', - 'Content-Length': body.length - }); - var options = { - base: 'https://marketplace.visualstudio.com/_apis/public/gallery', - requestOptions: { - method: 'POST', - gzip: true, - headers: headers, - body: body - } - }; - return remote('/extensionquery', options) - .pipe(flatmap(function (stream, f) { - var rawResult = f.contents.toString('utf8'); - var result = JSON.parse(rawResult); - var extension = result.results[0].extensions[0]; - if (!extension) { - return error("No such extension: " + extension); - } - var metadata = { - id: extension.extensionId, - publisherId: extension.publisher, - publisherDisplayName: extension.publisher.displayName - }; - var extensionVersion = extension.versions.filter(function (v) { return v.version === version; })[0]; - if (!extensionVersion) { - return error("No such extension version: " + extensionName + " @ " + version); - } - var asset = extensionVersion.files.filter(function (f) { return f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage'; })[0]; - if (!asset) { - return error("No VSIX found for extension version: " + extensionName + " @ " + version); - } - util.log('Downloading extension:', util.colors.yellow(extensionName + "@" + version), '...'); - var options = { - base: asset.source, - requestOptions: { - gzip: true, - headers: baseHeaders - } - }; - return remote('', options) - .pipe(flatmap(function (stream) { - var packageJsonFilter = filter('package.json', { restore: true }); - return stream - .pipe(vzip.src()) - .pipe(filter('extension/**')) - .pipe(rename(function (p) { return p.dirname = p.dirname.replace(/^extension\/?/, ''); })) - .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(json({ __metadata: metadata })) - .pipe(packageJsonFilter.restore); - })); - })); -} -exports.src = src; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var event_stream_1 = require("event-stream"); +var assign = require("object-assign"); +var remote = require("gulp-remote-src"); +var flatmap = require('gulp-flatmap'); +var vzip = require('gulp-vinyl-zip'); +var filter = require('gulp-filter'); +var rename = require('gulp-rename'); +var util = require('gulp-util'); +var buffer = require('gulp-buffer'); +var json = require('gulp-json-editor'); +function error(err) { + var result = event_stream_1.through(); + setTimeout(function () { return result.emit('error', err); }); + return result; +} +var baseHeaders = { + 'X-Market-Client-Id': 'VSCode Build', + 'User-Agent': 'VSCode Build', +}; +function src(extensionName, version) { + var filterType = 7; + var value = extensionName; + var criterium = { filterType: filterType, value: value }; + var criteria = [criterium]; + var pageNumber = 1; + var pageSize = 1; + var sortBy = 0; + var sortOrder = 0; + var flags = 0x1 | 0x2 | 0x80; + var assetTypes = ['Microsoft.VisualStudio.Services.VSIXPackage']; + var filters = [{ criteria: criteria, pageNumber: pageNumber, pageSize: pageSize, sortBy: sortBy, sortOrder: sortOrder }]; + var body = JSON.stringify({ filters: filters, assetTypes: assetTypes, flags: flags }); + var headers = assign({}, baseHeaders, { + 'Content-Type': 'application/json', + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Content-Length': body.length + }); + var options = { + base: 'https://marketplace.visualstudio.com/_apis/public/gallery', + requestOptions: { + method: 'POST', + gzip: true, + headers: headers, + body: body + } + }; + return remote('/extensionquery', options) + .pipe(flatmap(function (stream, f) { + var rawResult = f.contents.toString('utf8'); + var result = JSON.parse(rawResult); + var extension = result.results[0].extensions[0]; + if (!extension) { + return error("No such extension: " + extension); + } + var metadata = { + id: extension.extensionId, + publisherId: extension.publisher, + publisherDisplayName: extension.publisher.displayName + }; + var extensionVersion = extension.versions.filter(function (v) { return v.version === version; })[0]; + if (!extensionVersion) { + return error("No such extension version: " + extensionName + " @ " + version); + } + var asset = extensionVersion.files.filter(function (f) { return f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage'; })[0]; + if (!asset) { + return error("No VSIX found for extension version: " + extensionName + " @ " + version); + } + util.log('Downloading extension:', util.colors.yellow(extensionName + "@" + version), '...'); + var options = { + base: asset.source, + requestOptions: { + gzip: true, + headers: baseHeaders + } + }; + return remote('', options) + .pipe(flatmap(function (stream) { + var packageJsonFilter = filter('package.json', { restore: true }); + return stream + .pipe(vzip.src()) + .pipe(filter('extension/**')) + .pipe(rename(function (p) { return p.dirname = p.dirname.replace(/^extension\/?/, ''); })) + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json({ __metadata: metadata })) + .pipe(packageJsonFilter.restore); + })); + })); +} +exports.src = src; diff --git a/build/lib/git.js b/build/lib/git.js index e9a571b7c9b..b74fc78538a 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -1,53 +1,51 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -var path = require("path"); -var fs = require("fs"); -/** - * Returns the sha1 commit version of a repository or undefined in case of failure. - */ -function getVersion(repo) { - var git = path.join(repo, '.git'); - var headPath = path.join(git, 'HEAD'); - var head; - try { - head = fs.readFileSync(headPath, 'utf8').trim(); - } - catch (e) { - return void 0; - } - if (/^[0-9a-f]{40}$/i.test(head)) { - return head; - } - var refMatch = /^ref: (.*)$/.exec(head); - if (!refMatch) { - return void 0; - } - var ref = refMatch[1]; - var refPath = path.join(git, ref); - try { - return fs.readFileSync(refPath, 'utf8').trim(); - } - catch (e) { - // noop - } - var packedRefsPath = path.join(git, 'packed-refs'); - var refsRaw; - try { - refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); - } - catch (e) { - return void 0; - } - var refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; - var refsMatch; - var refs = {}; - while (refsMatch = refsRegex.exec(refsRaw)) { - refs[refsMatch[2]] = refsMatch[1]; - } - return refs[ref]; -} -exports.getVersion = getVersion; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var path = require("path"); +var fs = require("fs"); +/** + * Returns the sha1 commit version of a repository or undefined in case of failure. + */ +function getVersion(repo) { + var git = path.join(repo, '.git'); + var headPath = path.join(git, 'HEAD'); + var head; + try { + head = fs.readFileSync(headPath, 'utf8').trim(); + } + catch (e) { + return void 0; + } + if (/^[0-9a-f]{40}$/i.test(head)) { + return head; + } + var refMatch = /^ref: (.*)$/.exec(head); + if (!refMatch) { + return void 0; + } + var ref = refMatch[1]; + var refPath = path.join(git, ref); + try { + return fs.readFileSync(refPath, 'utf8').trim(); + } + catch (e) { + } + var packedRefsPath = path.join(git, 'packed-refs'); + var refsRaw; + try { + refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); + } + catch (e) { + return void 0; + } + var refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; + var refsMatch; + var refs = {}; + while (refsMatch = refsRegex.exec(refsRaw)) { + refs[refsMatch[2]] = refsMatch[1]; + } + return refs[ref]; +} +exports.getVersion = getVersion; diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 7dcf0a0b763..6af36a8ea48 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -1,290 +1,289 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var path = require("path"); -var fs = require("fs"); -var event_stream_1 = require("event-stream"); -var File = require("vinyl"); -var Is = require("is"); -var util = require('gulp-util'); -function log(message) { - var rest = []; - for (var _i = 1; _i < arguments.length; _i++) { - rest[_i - 1] = arguments[_i]; - } - util.log.apply(util, [util.colors.green('[i18n]'), message].concat(rest)); -} -var LocalizeInfo; -(function (LocalizeInfo) { - function is(value) { - var candidate = value; - return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(function (element) { return Is.string(element); }))); - } - LocalizeInfo.is = is; -})(LocalizeInfo || (LocalizeInfo = {})); -var BundledFormat; -(function (BundledFormat) { - function is(value) { - if (Is.undef(value)) { - return false; - } - var candidate = value; - var length = Object.keys(value).length; - return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); - } - BundledFormat.is = is; -})(BundledFormat || (BundledFormat = {})); -var vscodeLanguages = [ - 'chs', - 'cht', - 'jpn', - 'kor', - 'deu', - 'fra', - 'esn', - 'rus', - 'ita' -]; -var iso639_3_to_2 = { - 'chs': 'zh-cn', - 'cht': 'zh-tw', - 'csy': 'cs-cz', - 'deu': 'de', - 'enu': 'en', - 'esn': 'es', - 'fra': 'fr', - 'hun': 'hu', - 'ita': 'it', - 'jpn': 'ja', - 'kor': 'ko', - 'nld': 'nl', - 'plk': 'pl', - 'ptb': 'pt-br', - 'ptg': 'pt', - 'rus': 'ru', - 'sve': 'sv-se', - 'trk': 'tr' -}; -function sortLanguages(directoryNames) { - return directoryNames.map(function (dirName) { - var lower = dirName.toLowerCase(); - return { - name: lower, - iso639_2: iso639_3_to_2[lower] - }; - }).sort(function (a, b) { - if (!a.iso639_2 && !b.iso639_2) { - return 0; - } - if (!a.iso639_2) { - return -1; - } - if (!b.iso639_2) { - return 1; - } - return a.iso639_2 < b.iso639_2 ? -1 : (a.iso639_2 > b.iso639_2 ? 1 : 0); - }); -} -function stripComments(content) { - /** - * First capturing group matches double quoted string - * Second matches single quotes string - * Third matches block comments - * Fourth matches line comments - */ - var regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; - var result = content.replace(regexp, function (match, m1, m2, m3, m4) { - // Only one of m1, m2, m3, m4 matches - if (m3) { - // A block comment. Replace with nothing - return ''; - } - else if (m4) { - // A line comment. If it ends in \r?\n then keep it. - var length_1 = m4.length; - if (length_1 > 2 && m4[length_1 - 1] === '\n') { - return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; - } - else { - return ''; - } - } - else { - // We match a string - return match; - } - }); - return result; -} -; -function escapeCharacters(value) { - var result = []; - for (var i = 0; i < value.length; i++) { - var ch = value.charAt(i); - switch (ch) { - case '\'': - result.push('\\\''); - break; - case '"': - result.push('\\"'); - break; - case '\\': - result.push('\\\\'); - break; - case '\n': - result.push('\\n'); - break; - case '\r': - result.push('\\r'); - break; - case '\t': - result.push('\\t'); - break; - case '\b': - result.push('\\b'); - break; - case '\f': - result.push('\\f'); - break; - default: - result.push(ch); - } - } - return result.join(''); -} -function processCoreBundleFormat(fileHeader, json, emitter) { - var keysSection = json.keys; - var messageSection = json.messages; - var bundleSection = json.bundles; - var statistics = Object.create(null); - var total = 0; - var defaultMessages = Object.create(null); - var modules = Object.keys(keysSection); - modules.forEach(function (module) { - var keys = keysSection[module]; - var messages = messageSection[module]; - if (!messages || keys.length !== messages.length) { - emitter.emit('error', "Message for module " + module + " corrupted. Mismatch in number of keys and messages."); - return; - } - var messageMap = Object.create(null); - defaultMessages[module] = messageMap; - keys.map(function (key, i) { - total++; - if (Is.string(key)) { - messageMap[key] = messages[i]; - } - else { - messageMap[key.key] = messages[i]; - } - }); - }); - var languageDirectory = path.join(__dirname, '..', '..', 'i18n'); - var languages = sortLanguages(fs.readdirSync(languageDirectory).filter(function (item) { return fs.statSync(path.join(languageDirectory, item)).isDirectory(); })); - languages.forEach(function (language) { - if (!language.iso639_2) { - return; - } - if (process.env['VSCODE_BUILD_VERBOSE']) { - log("Generating nls bundles for: " + language.iso639_2); - } - statistics[language.iso639_2] = 0; - var localizedModules = Object.create(null); - var cwd = path.join(languageDirectory, language.name, 'src'); - modules.forEach(function (module) { - var order = keysSection[module]; - var i18nFile = path.join(cwd, module) + '.i18n.json'; - var messages = null; - if (fs.existsSync(i18nFile)) { - var content = stripComments(fs.readFileSync(i18nFile, 'utf8')); - messages = JSON.parse(content); - } - else { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log("No localized messages found for module " + module + ". Using default messages."); - } - messages = defaultMessages[module]; - statistics[language.iso639_2] = statistics[language.iso639_2] + Object.keys(messages).length; - } - var localizedMessages = []; - order.forEach(function (keyInfo) { - var key = null; - if (Is.string(keyInfo)) { - key = keyInfo; - } - else { - key = keyInfo.key; - } - var message = messages[key]; - if (!message) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log("No localized message found for key " + key + " in module " + module + ". Using default message."); - } - message = defaultMessages[module][key]; - statistics[language.iso639_2] = statistics[language.iso639_2] + 1; - } - localizedMessages.push(message); - }); - localizedModules[module] = localizedMessages; - }); - Object.keys(bundleSection).forEach(function (bundle) { - var modules = bundleSection[bundle]; - var contents = [ - fileHeader, - "define(\"" + bundle + ".nls." + language.iso639_2 + "\", {" - ]; - modules.forEach(function (module, index) { - contents.push("\t\"" + module + "\": ["); - var messages = localizedModules[module]; - if (!messages) { - emitter.emit('error', "Didn't find messages for module " + module + "."); - return; - } - messages.forEach(function (message, index) { - contents.push("\t\t\"" + escapeCharacters(message) + (index < messages.length ? '",' : '"')); - }); - contents.push(index < modules.length - 1 ? '\t],' : '\t]'); - }); - contents.push('});'); - emitter.emit('data', new File({ path: bundle + '.nls.' + language.iso639_2 + '.js', contents: new Buffer(contents.join('\n'), 'utf-8') })); - }); - }); - Object.keys(statistics).forEach(function (key) { - var value = statistics[key]; - log(key + " has " + value + " untranslated strings."); - }); - vscodeLanguages.forEach(function (language) { - var iso639_2 = iso639_3_to_2[language]; - if (!iso639_2) { - log("\tCouldn't find iso639 2 mapping for language " + language + ". Using default language instead."); - } - else { - var stats = statistics[iso639_2]; - if (Is.undef(stats)) { - log("\tNo translations found for language " + language + ". Using default language instead."); - } - } - }); -} -function processNlsFiles(opts) { - return event_stream_1.through(function (file) { - var fileName = path.basename(file.path); - if (fileName === 'nls.metadata.json') { - var json = null; - if (file.isBuffer()) { - json = JSON.parse(file.contents.toString('utf8')); - } - else { - this.emit('error', "Failed to read component file: " + file.relative); - } - if (BundledFormat.is(json)) { - processCoreBundleFormat(opts.fileHeader, json, this); - } - } - this.emit('data', file); - }); -} -exports.processNlsFiles = processNlsFiles; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var path = require("path"); +var fs = require("fs"); +var event_stream_1 = require("event-stream"); +var File = require("vinyl"); +var Is = require("is"); +var util = require('gulp-util'); +function log(message) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + util.log.apply(util, [util.colors.green('[i18n]'), message].concat(rest)); +} +var LocalizeInfo; +(function (LocalizeInfo) { + function is(value) { + var candidate = value; + return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(function (element) { return Is.string(element); }))); + } + LocalizeInfo.is = is; +})(LocalizeInfo || (LocalizeInfo = {})); +var BundledFormat; +(function (BundledFormat) { + function is(value) { + if (Is.undef(value)) { + return false; + } + var candidate = value; + var length = Object.keys(value).length; + return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); + } + BundledFormat.is = is; +})(BundledFormat || (BundledFormat = {})); +var vscodeLanguages = [ + 'chs', + 'cht', + 'jpn', + 'kor', + 'deu', + 'fra', + 'esn', + 'rus', + 'ita' +]; +var iso639_3_to_2 = { + 'chs': 'zh-cn', + 'cht': 'zh-tw', + 'csy': 'cs-cz', + 'deu': 'de', + 'enu': 'en', + 'esn': 'es', + 'fra': 'fr', + 'hun': 'hu', + 'ita': 'it', + 'jpn': 'ja', + 'kor': 'ko', + 'nld': 'nl', + 'plk': 'pl', + 'ptb': 'pt-br', + 'ptg': 'pt', + 'rus': 'ru', + 'sve': 'sv-se', + 'trk': 'tr' +}; +function sortLanguages(directoryNames) { + return directoryNames.map(function (dirName) { + var lower = dirName.toLowerCase(); + return { + name: lower, + iso639_2: iso639_3_to_2[lower] + }; + }).sort(function (a, b) { + if (!a.iso639_2 && !b.iso639_2) { + return 0; + } + if (!a.iso639_2) { + return -1; + } + if (!b.iso639_2) { + return 1; + } + return a.iso639_2 < b.iso639_2 ? -1 : (a.iso639_2 > b.iso639_2 ? 1 : 0); + }); +} +function stripComments(content) { + /** + * First capturing group matches double quoted string + * Second matches single quotes string + * Third matches block comments + * Fourth matches line comments + */ + var regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + var result = content.replace(regexp, function (match, m1, m2, m3, m4) { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } + else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + var length_1 = m4.length; + if (length_1 > 2 && m4[length_1 - 1] === '\n') { + return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; + } + else { + return ''; + } + } + else { + // We match a string + return match; + } + }); + return result; +} +; +function escapeCharacters(value) { + var result = []; + for (var i = 0; i < value.length; i++) { + var ch = value.charAt(i); + switch (ch) { + case '\'': + result.push('\\\''); + break; + case '"': + result.push('\\"'); + break; + case '\\': + result.push('\\\\'); + break; + case '\n': + result.push('\\n'); + break; + case '\r': + result.push('\\r'); + break; + case '\t': + result.push('\\t'); + break; + case '\b': + result.push('\\b'); + break; + case '\f': + result.push('\\f'); + break; + default: + result.push(ch); + } + } + return result.join(''); +} +function processCoreBundleFormat(fileHeader, json, emitter) { + var keysSection = json.keys; + var messageSection = json.messages; + var bundleSection = json.bundles; + var statistics = Object.create(null); + var total = 0; + var defaultMessages = Object.create(null); + var modules = Object.keys(keysSection); + modules.forEach(function (module) { + var keys = keysSection[module]; + var messages = messageSection[module]; + if (!messages || keys.length !== messages.length) { + emitter.emit('error', "Message for module " + module + " corrupted. Mismatch in number of keys and messages."); + return; + } + var messageMap = Object.create(null); + defaultMessages[module] = messageMap; + keys.map(function (key, i) { + total++; + if (Is.string(key)) { + messageMap[key] = messages[i]; + } + else { + messageMap[key.key] = messages[i]; + } + }); + }); + var languageDirectory = path.join(__dirname, '..', '..', 'i18n'); + var languages = sortLanguages(fs.readdirSync(languageDirectory).filter(function (item) { return fs.statSync(path.join(languageDirectory, item)).isDirectory(); })); + languages.forEach(function (language) { + if (!language.iso639_2) { + return; + } + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("Generating nls bundles for: " + language.iso639_2); + } + statistics[language.iso639_2] = 0; + var localizedModules = Object.create(null); + var cwd = path.join(languageDirectory, language.name, 'src'); + modules.forEach(function (module) { + var order = keysSection[module]; + var i18nFile = path.join(cwd, module) + '.i18n.json'; + var messages = null; + if (fs.existsSync(i18nFile)) { + var content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + messages = JSON.parse(content); + } + else { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("No localized messages found for module " + module + ". Using default messages."); + } + messages = defaultMessages[module]; + statistics[language.iso639_2] = statistics[language.iso639_2] + Object.keys(messages).length; + } + var localizedMessages = []; + order.forEach(function (keyInfo) { + var key = null; + if (Is.string(keyInfo)) { + key = keyInfo; + } + else { + key = keyInfo.key; + } + var message = messages[key]; + if (!message) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log("No localized message found for key " + key + " in module " + module + ". Using default message."); + } + message = defaultMessages[module][key]; + statistics[language.iso639_2] = statistics[language.iso639_2] + 1; + } + localizedMessages.push(message); + }); + localizedModules[module] = localizedMessages; + }); + Object.keys(bundleSection).forEach(function (bundle) { + var modules = bundleSection[bundle]; + var contents = [ + fileHeader, + "define(\"" + bundle + ".nls." + language.iso639_2 + "\", {" + ]; + modules.forEach(function (module, index) { + contents.push("\t\"" + module + "\": ["); + var messages = localizedModules[module]; + if (!messages) { + emitter.emit('error', "Didn't find messages for module " + module + "."); + return; + } + messages.forEach(function (message, index) { + contents.push("\t\t\"" + escapeCharacters(message) + (index < messages.length ? '",' : '"')); + }); + contents.push(index < modules.length - 1 ? '\t],' : '\t]'); + }); + contents.push('});'); + emitter.emit('data', new File({ path: bundle + '.nls.' + language.iso639_2 + '.js', contents: new Buffer(contents.join('\n'), 'utf-8') })); + }); + }); + Object.keys(statistics).forEach(function (key) { + var value = statistics[key]; + log(key + " has " + value + " untranslated strings."); + }); + vscodeLanguages.forEach(function (language) { + var iso639_2 = iso639_3_to_2[language]; + if (!iso639_2) { + log("\tCouldn't find iso639 2 mapping for language " + language + ". Using default language instead."); + } + else { + var stats = statistics[iso639_2]; + if (Is.undef(stats)) { + log("\tNo translations found for language " + language + ". Using default language instead."); + } + } + }); +} +function processNlsFiles(opts) { + return event_stream_1.through(function (file) { + var fileName = path.basename(file.path); + if (fileName === 'nls.metadata.json') { + var json = null; + if (file.isBuffer()) { + json = JSON.parse(file.contents.toString('utf8')); + } + else { + this.emit('error', "Failed to read component file: " + file.relative); + } + if (BundledFormat.is(json)) { + processCoreBundleFormat(opts.fileHeader, json, this); + } + } + this.emit('data', file); + }); +} +exports.processNlsFiles = processNlsFiles; diff --git a/build/lib/nls.js b/build/lib/nls.js index da8e6a27a53..e695200d1bd 100644 --- a/build/lib/nls.js +++ b/build/lib/nls.js @@ -1,355 +1,355 @@ -"use strict"; -var ts = require("./typescript/typescriptServices"); -var lazy = require("lazy.js"); -var event_stream_1 = require("event-stream"); -var File = require("vinyl"); -var sm = require("source-map"); -var assign = require("object-assign"); -var path = require("path"); -var CollectStepResult; -(function (CollectStepResult) { - CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes"; - CollectStepResult[CollectStepResult["YesAndRecurse"] = 1] = "YesAndRecurse"; - CollectStepResult[CollectStepResult["No"] = 2] = "No"; - CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse"; -})(CollectStepResult || (CollectStepResult = {})); -function collect(node, fn) { - var result = []; - function loop(node) { - var stepResult = fn(node); - if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) { - result.push(node); - } - if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) { - ts.forEachChild(node, loop); - } - } - loop(node); - return result; -} -function clone(object) { - var result = {}; - for (var id in object) { - result[id] = object[id]; - } - return result; -} -function template(lines) { - var indent = '', wrap = ''; - if (lines.length > 1) { - indent = '\t'; - wrap = '\n'; - } - return "/*---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------*/\ndefine([], [" + (wrap + lines.map(function (l) { return indent + l; }).join(',\n') + wrap) + "]);"; -} -/** - * Returns a stream containing the patched JavaScript and source maps. - */ -function nls() { - var input = event_stream_1.through(); - var output = input.pipe(event_stream_1.through(function (f) { - var _this = this; - if (!f.sourceMap) { - return this.emit('error', new Error("File " + f.relative + " does not have sourcemaps.")); - } - var source = f.sourceMap.sources[0]; - if (!source) { - return this.emit('error', new Error("File " + f.relative + " does not have a source in the source map.")); - } - var root = f.sourceMap.sourceRoot; - if (root) { - source = path.join(root, source); - } - var typescript = f.sourceMap.sourcesContent[0]; - if (!typescript) { - return this.emit('error', new Error("File " + f.relative + " does not have the original content in the source map.")); - } - nls.patchFiles(f, typescript).forEach(function (f) { return _this.emit('data', f); }); - })); - return event_stream_1.duplex(input, output); -} -function isImportNode(node) { - return node.kind === 212 /* ImportDeclaration */ || node.kind === 211 /* ImportEqualsDeclaration */; -} -(function (nls_1) { - function fileFrom(file, contents, path) { - if (path === void 0) { path = file.path; } - return new File({ - contents: new Buffer(contents), - base: file.base, - cwd: file.cwd, - path: path - }); - } - nls_1.fileFrom = fileFrom; - function mappedPositionFrom(source, lc) { - return { source: source, line: lc.line + 1, column: lc.character }; - } - nls_1.mappedPositionFrom = mappedPositionFrom; - function lcFrom(position) { - return { line: position.line - 1, character: position.column }; - } - nls_1.lcFrom = lcFrom; - var SingleFileServiceHost = (function () { - function SingleFileServiceHost(options, filename, contents) { - var _this = this; - this.options = options; - this.filename = filename; - this.getCompilationSettings = function () { return _this.options; }; - this.getScriptFileNames = function () { return [_this.filename]; }; - this.getScriptVersion = function () { return '1'; }; - this.getScriptSnapshot = function (name) { return name === _this.filename ? _this.file : _this.lib; }; - this.getCurrentDirectory = function () { return ''; }; - this.getDefaultLibFileName = function () { return 'lib.d.ts'; }; - this.file = ts.ScriptSnapshot.fromString(contents); - this.lib = ts.ScriptSnapshot.fromString(''); - } - return SingleFileServiceHost; - }()); - nls_1.SingleFileServiceHost = SingleFileServiceHost; - function isCallExpressionWithinTextSpanCollectStep(textSpan, node) { - if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) { - return CollectStepResult.No; - } - return node.kind === 160 /* CallExpression */ ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; - } - function analyze(contents, options) { - if (options === void 0) { options = {}; } - var filename = 'file.ts'; - var serviceHost = new SingleFileServiceHost(assign(clone(options), { noResolve: true }), filename, contents); - var service = ts.createLanguageService(serviceHost); - var sourceFile = service.getSourceFile(filename); - // all imports - var imports = lazy(collect(sourceFile, function (n) { return isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; })); - // import nls = require('vs/nls'); - var importEqualsDeclarations = imports - .filter(function (n) { return n.kind === 211 /* ImportEqualsDeclaration */; }) - .map(function (n) { return n; }) - .filter(function (d) { return d.moduleReference.kind === 222 /* ExternalModuleReference */; }) - .filter(function (d) { return d.moduleReference.expression.getText() === '\'vs/nls\''; }); - // import ... from 'vs/nls'; - var importDeclarations = imports - .filter(function (n) { return n.kind === 212 /* ImportDeclaration */; }) - .map(function (n) { return n; }) - .filter(function (d) { return d.moduleSpecifier.kind === 8 /* StringLiteral */; }) - .filter(function (d) { return d.moduleSpecifier.getText() === '\'vs/nls\''; }) - .filter(function (d) { return !!d.importClause && !!d.importClause.namedBindings; }); - var nlsExpressions = importEqualsDeclarations - .map(function (d) { return d.moduleReference.expression; }) - .concat(importDeclarations.map(function (d) { return d.moduleSpecifier; })) - .map(function (d) { return ({ - start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), - end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) - }); }); - // `nls.localize(...)` calls - var nlsLocalizeCallExpressions = importDeclarations - .filter(function (d) { return d.importClause.namedBindings.kind === 214 /* NamespaceImport */; }) - .map(function (d) { return d.importClause.namedBindings.name; }) - .concat(importEqualsDeclarations.map(function (d) { return d.name; })) - .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) - .flatten() - .filter(function (r) { return !r.isWriteAccess; }) - .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) - .map(function (a) { return lazy(a).last(); }) - .filter(function (n) { return !!n; }) - .map(function (n) { return n; }) - .filter(function (n) { return n.expression.kind === 158 /* PropertyAccessExpression */ && n.expression.name.getText() === 'localize'; }); - // `localize` named imports - var allLocalizeImportDeclarations = importDeclarations - .filter(function (d) { return d.importClause.namedBindings.kind === 215 /* NamedImports */; }) - .map(function (d) { return d.importClause.namedBindings.elements; }) - .flatten(); - // `localize` read-only references - var localizeReferences = allLocalizeImportDeclarations - .filter(function (d) { return d.name.getText() === 'localize'; }) - .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) - .flatten() - .filter(function (r) { return !r.isWriteAccess; }); - // custom named `localize` read-only references - var namedLocalizeReferences = allLocalizeImportDeclarations - .filter(function (d) { return d.propertyName && d.propertyName.getText() === 'localize'; }) - .map(function (n) { return service.getReferencesAtPosition(filename, n.name.pos + 1); }) - .flatten() - .filter(function (r) { return !r.isWriteAccess; }); - // find the deepest call expressions AST nodes that contain those references - var localizeCallExpressions = localizeReferences - .concat(namedLocalizeReferences) - .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) - .map(function (a) { return lazy(a).last(); }) - .filter(function (n) { return !!n; }) - .map(function (n) { return n; }); - // collect everything - var localizeCalls = nlsLocalizeCallExpressions - .concat(localizeCallExpressions) - .map(function (e) { return e.arguments; }) - .filter(function (a) { return a.length > 1; }) - .sort(function (a, b) { return a[0].getStart() - b[0].getStart(); }) - .map(function (a) { return ({ - keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) }, - key: a[0].getText(), - valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) }, - value: a[1].getText() - }); }); - return { - localizeCalls: localizeCalls.toArray(), - nlsExpressions: nlsExpressions.toArray() - }; - } - nls_1.analyze = analyze; - var TextModel = (function () { - function TextModel(contents) { - var regex = /\r\n|\r|\n/g; - var index = 0; - var match; - this.lines = []; - this.lineEndings = []; - while (match = regex.exec(contents)) { - this.lines.push(contents.substring(index, match.index)); - this.lineEndings.push(match[0]); - index = regex.lastIndex; - } - if (contents.length > 0) { - this.lines.push(contents.substring(index, contents.length)); - this.lineEndings.push(''); - } - } - TextModel.prototype.get = function (index) { - return this.lines[index]; - }; - TextModel.prototype.set = function (index, line) { - this.lines[index] = line; - }; - Object.defineProperty(TextModel.prototype, "lineCount", { - get: function () { - return this.lines.length; - }, - enumerable: true, - configurable: true - }); - /** - * Applies patch(es) to the model. - * Multiple patches must be ordered. - * Does not support patches spanning multiple lines. - */ - TextModel.prototype.apply = function (patch) { - var startLineNumber = patch.span.start.line; - var endLineNumber = patch.span.end.line; - var startLine = this.lines[startLineNumber] || ''; - var endLine = this.lines[endLineNumber] || ''; - this.lines[startLineNumber] = [ - startLine.substring(0, patch.span.start.character), - patch.content, - endLine.substring(patch.span.end.character) - ].join(''); - for (var i = startLineNumber + 1; i <= endLineNumber; i++) { - this.lines[i] = ''; - } - }; - TextModel.prototype.toString = function () { - return lazy(this.lines).zip(this.lineEndings) - .flatten().toArray().join(''); - }; - return TextModel; - }()); - nls_1.TextModel = TextModel; - function patchJavascript(patches, contents, moduleId) { - var model = new nls.TextModel(contents); - // patch the localize calls - lazy(patches).reverse().each(function (p) { return model.apply(p); }); - // patch the 'vs/nls' imports - var firstLine = model.get(0); - var patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); - model.set(0, patchedFirstLine); - return model.toString(); - } - nls_1.patchJavascript = patchJavascript; - function patchSourcemap(patches, rsm, smc) { - var smg = new sm.SourceMapGenerator({ - file: rsm.file, - sourceRoot: rsm.sourceRoot - }); - patches = patches.reverse(); - var currentLine = -1; - var currentLineDiff = 0; - var source = null; - smc.eachMapping(function (m) { - var patch = patches[patches.length - 1]; - var original = { line: m.originalLine, column: m.originalColumn }; - var generated = { line: m.generatedLine, column: m.generatedColumn }; - if (currentLine !== generated.line) { - currentLineDiff = 0; - } - currentLine = generated.line; - generated.column += currentLineDiff; - if (patch && m.generatedLine - 1 === patch.span.end.line && m.generatedColumn === patch.span.end.character) { - var originalLength = patch.span.end.character - patch.span.start.character; - var modifiedLength = patch.content.length; - var lengthDiff = modifiedLength - originalLength; - currentLineDiff += lengthDiff; - generated.column += lengthDiff; - patches.pop(); - } - source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; - source = source.replace(/\\/g, '/'); - smg.addMapping({ source: source, name: m.name, original: original, generated: generated }); - }, null, sm.SourceMapConsumer.GENERATED_ORDER); - if (source) { - smg.setSourceContent(source, smc.sourceContentFor(source)); - } - return JSON.parse(smg.toString()); - } - nls_1.patchSourcemap = patchSourcemap; - function patch(moduleId, typescript, javascript, sourcemap) { - var _a = analyze(typescript), localizeCalls = _a.localizeCalls, nlsExpressions = _a.nlsExpressions; - if (localizeCalls.length === 0) { - return { javascript: javascript, sourcemap: sourcemap }; - } - var nlsKeys = template(localizeCalls.map(function (lc) { return lc.key; })); - var nls = template(localizeCalls.map(function (lc) { return lc.value; })); - var smc = new sm.SourceMapConsumer(sourcemap); - var positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); - var i = 0; - // build patches - var patches = lazy(localizeCalls) - .map(function (lc) { return ([ - { range: lc.keySpan, content: '' + (i++) }, - { range: lc.valueSpan, content: 'null' } - ]); }) - .flatten() - .map(function (c) { - var start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start))); - var end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); - return { span: { start: start, end: end }, content: c.content }; - }) - .toArray(); - javascript = patchJavascript(patches, javascript, moduleId); - // since imports are not within the sourcemap information, - // we must do this MacGyver style - if (nlsExpressions.length) { - javascript = javascript.replace(/^define\(.*$/m, function (line) { - return line.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); - }); - } - sourcemap = patchSourcemap(patches, sourcemap, smc); - return { javascript: javascript, sourcemap: sourcemap, nlsKeys: nlsKeys, nls: nls }; - } - nls_1.patch = patch; - function patchFiles(javascriptFile, typescript) { - // hack? - var moduleId = javascriptFile.relative - .replace(/\.js$/, '') - .replace(/\\/g, '/'); - var _a = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap), javascript = _a.javascript, sourcemap = _a.sourcemap, nlsKeys = _a.nlsKeys, nls = _a.nls; - var result = [fileFrom(javascriptFile, javascript)]; - result[0].sourceMap = sourcemap; - if (nlsKeys) { - result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); - } - if (nls) { - result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); - } - return result; - } - nls_1.patchFiles = patchFiles; -})(nls || (nls = {})); -module.exports = nls; +"use strict"; +var ts = require("./typescript/typescriptServices"); +var lazy = require("lazy.js"); +var event_stream_1 = require("event-stream"); +var File = require("vinyl"); +var sm = require("source-map"); +var assign = require("object-assign"); +var path = require("path"); +var CollectStepResult; +(function (CollectStepResult) { + CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes"; + CollectStepResult[CollectStepResult["YesAndRecurse"] = 1] = "YesAndRecurse"; + CollectStepResult[CollectStepResult["No"] = 2] = "No"; + CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse"; +})(CollectStepResult || (CollectStepResult = {})); +function collect(node, fn) { + var result = []; + function loop(node) { + var stepResult = fn(node); + if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) { + result.push(node); + } + if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) { + ts.forEachChild(node, loop); + } + } + loop(node); + return result; +} +function clone(object) { + var result = {}; + for (var id in object) { + result[id] = object[id]; + } + return result; +} +function template(lines) { + var indent = '', wrap = ''; + if (lines.length > 1) { + indent = '\t'; + wrap = '\n'; + } + return "/*---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------*/\ndefine([], [" + (wrap + lines.map(function (l) { return indent + l; }).join(',\n') + wrap) + "]);"; +} +/** + * Returns a stream containing the patched JavaScript and source maps. + */ +function nls() { + var input = event_stream_1.through(); + var output = input.pipe(event_stream_1.through(function (f) { + var _this = this; + if (!f.sourceMap) { + return this.emit('error', new Error("File " + f.relative + " does not have sourcemaps.")); + } + var source = f.sourceMap.sources[0]; + if (!source) { + return this.emit('error', new Error("File " + f.relative + " does not have a source in the source map.")); + } + var root = f.sourceMap.sourceRoot; + if (root) { + source = path.join(root, source); + } + var typescript = f.sourceMap.sourcesContent[0]; + if (!typescript) { + return this.emit('error', new Error("File " + f.relative + " does not have the original content in the source map.")); + } + nls.patchFiles(f, typescript).forEach(function (f) { return _this.emit('data', f); }); + })); + return event_stream_1.duplex(input, output); +} +function isImportNode(node) { + return node.kind === 212 /* ImportDeclaration */ || node.kind === 211 /* ImportEqualsDeclaration */; +} +(function (nls_1) { + function fileFrom(file, contents, path) { + if (path === void 0) { path = file.path; } + return new File({ + contents: new Buffer(contents), + base: file.base, + cwd: file.cwd, + path: path + }); + } + nls_1.fileFrom = fileFrom; + function mappedPositionFrom(source, lc) { + return { source: source, line: lc.line + 1, column: lc.character }; + } + nls_1.mappedPositionFrom = mappedPositionFrom; + function lcFrom(position) { + return { line: position.line - 1, character: position.column }; + } + nls_1.lcFrom = lcFrom; + var SingleFileServiceHost = (function () { + function SingleFileServiceHost(options, filename, contents) { + var _this = this; + this.options = options; + this.filename = filename; + this.getCompilationSettings = function () { return _this.options; }; + this.getScriptFileNames = function () { return [_this.filename]; }; + this.getScriptVersion = function () { return '1'; }; + this.getScriptSnapshot = function (name) { return name === _this.filename ? _this.file : _this.lib; }; + this.getCurrentDirectory = function () { return ''; }; + this.getDefaultLibFileName = function () { return 'lib.d.ts'; }; + this.file = ts.ScriptSnapshot.fromString(contents); + this.lib = ts.ScriptSnapshot.fromString(''); + } + return SingleFileServiceHost; + }()); + nls_1.SingleFileServiceHost = SingleFileServiceHost; + function isCallExpressionWithinTextSpanCollectStep(textSpan, node) { + if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) { + return CollectStepResult.No; + } + return node.kind === 160 /* CallExpression */ ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; + } + function analyze(contents, options) { + if (options === void 0) { options = {}; } + var filename = 'file.ts'; + var serviceHost = new SingleFileServiceHost(assign(clone(options), { noResolve: true }), filename, contents); + var service = ts.createLanguageService(serviceHost); + var sourceFile = service.getSourceFile(filename); + // all imports + var imports = lazy(collect(sourceFile, function (n) { return isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse; })); + // import nls = require('vs/nls'); + var importEqualsDeclarations = imports + .filter(function (n) { return n.kind === 211 /* ImportEqualsDeclaration */; }) + .map(function (n) { return n; }) + .filter(function (d) { return d.moduleReference.kind === 222 /* ExternalModuleReference */; }) + .filter(function (d) { return d.moduleReference.expression.getText() === '\'vs/nls\''; }); + // import ... from 'vs/nls'; + var importDeclarations = imports + .filter(function (n) { return n.kind === 212 /* ImportDeclaration */; }) + .map(function (n) { return n; }) + .filter(function (d) { return d.moduleSpecifier.kind === 8 /* StringLiteral */; }) + .filter(function (d) { return d.moduleSpecifier.getText() === '\'vs/nls\''; }) + .filter(function (d) { return !!d.importClause && !!d.importClause.namedBindings; }); + var nlsExpressions = importEqualsDeclarations + .map(function (d) { return d.moduleReference.expression; }) + .concat(importDeclarations.map(function (d) { return d.moduleSpecifier; })) + .map(function (d) { return ({ + start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), + end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) + }); }); + // `nls.localize(...)` calls + var nlsLocalizeCallExpressions = importDeclarations + .filter(function (d) { return d.importClause.namedBindings.kind === 214 /* NamespaceImport */; }) + .map(function (d) { return d.importClause.namedBindings.name; }) + .concat(importEqualsDeclarations.map(function (d) { return d.name; })) + .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }) + .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) + .map(function (a) { return lazy(a).last(); }) + .filter(function (n) { return !!n; }) + .map(function (n) { return n; }) + .filter(function (n) { return n.expression.kind === 158 /* PropertyAccessExpression */ && n.expression.name.getText() === 'localize'; }); + // `localize` named imports + var allLocalizeImportDeclarations = importDeclarations + .filter(function (d) { return d.importClause.namedBindings.kind === 215 /* NamedImports */; }) + .map(function (d) { return d.importClause.namedBindings.elements; }) + .flatten(); + // `localize` read-only references + var localizeReferences = allLocalizeImportDeclarations + .filter(function (d) { return d.name.getText() === 'localize'; }) + .map(function (n) { return service.getReferencesAtPosition(filename, n.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }); + // custom named `localize` read-only references + var namedLocalizeReferences = allLocalizeImportDeclarations + .filter(function (d) { return d.propertyName && d.propertyName.getText() === 'localize'; }) + .map(function (n) { return service.getReferencesAtPosition(filename, n.name.pos + 1); }) + .flatten() + .filter(function (r) { return !r.isWriteAccess; }); + // find the deepest call expressions AST nodes that contain those references + var localizeCallExpressions = localizeReferences + .concat(namedLocalizeReferences) + .map(function (r) { return collect(sourceFile, function (n) { return isCallExpressionWithinTextSpanCollectStep(r.textSpan, n); }); }) + .map(function (a) { return lazy(a).last(); }) + .filter(function (n) { return !!n; }) + .map(function (n) { return n; }); + // collect everything + var localizeCalls = nlsLocalizeCallExpressions + .concat(localizeCallExpressions) + .map(function (e) { return e.arguments; }) + .filter(function (a) { return a.length > 1; }) + .sort(function (a, b) { return a[0].getStart() - b[0].getStart(); }) + .map(function (a) { return ({ + keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) }, + key: a[0].getText(), + valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) }, + value: a[1].getText() + }); }); + return { + localizeCalls: localizeCalls.toArray(), + nlsExpressions: nlsExpressions.toArray() + }; + } + nls_1.analyze = analyze; + var TextModel = (function () { + function TextModel(contents) { + var regex = /\r\n|\r|\n/g; + var index = 0; + var match; + this.lines = []; + this.lineEndings = []; + while (match = regex.exec(contents)) { + this.lines.push(contents.substring(index, match.index)); + this.lineEndings.push(match[0]); + index = regex.lastIndex; + } + if (contents.length > 0) { + this.lines.push(contents.substring(index, contents.length)); + this.lineEndings.push(''); + } + } + TextModel.prototype.get = function (index) { + return this.lines[index]; + }; + TextModel.prototype.set = function (index, line) { + this.lines[index] = line; + }; + Object.defineProperty(TextModel.prototype, "lineCount", { + get: function () { + return this.lines.length; + }, + enumerable: true, + configurable: true + }); + /** + * Applies patch(es) to the model. + * Multiple patches must be ordered. + * Does not support patches spanning multiple lines. + */ + TextModel.prototype.apply = function (patch) { + var startLineNumber = patch.span.start.line; + var endLineNumber = patch.span.end.line; + var startLine = this.lines[startLineNumber] || ''; + var endLine = this.lines[endLineNumber] || ''; + this.lines[startLineNumber] = [ + startLine.substring(0, patch.span.start.character), + patch.content, + endLine.substring(patch.span.end.character) + ].join(''); + for (var i = startLineNumber + 1; i <= endLineNumber; i++) { + this.lines[i] = ''; + } + }; + TextModel.prototype.toString = function () { + return lazy(this.lines).zip(this.lineEndings) + .flatten().toArray().join(''); + }; + return TextModel; + }()); + nls_1.TextModel = TextModel; + function patchJavascript(patches, contents, moduleId) { + var model = new nls.TextModel(contents); + // patch the localize calls + lazy(patches).reverse().each(function (p) { return model.apply(p); }); + // patch the 'vs/nls' imports + var firstLine = model.get(0); + var patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); + model.set(0, patchedFirstLine); + return model.toString(); + } + nls_1.patchJavascript = patchJavascript; + function patchSourcemap(patches, rsm, smc) { + var smg = new sm.SourceMapGenerator({ + file: rsm.file, + sourceRoot: rsm.sourceRoot + }); + patches = patches.reverse(); + var currentLine = -1; + var currentLineDiff = 0; + var source = null; + smc.eachMapping(function (m) { + var patch = patches[patches.length - 1]; + var original = { line: m.originalLine, column: m.originalColumn }; + var generated = { line: m.generatedLine, column: m.generatedColumn }; + if (currentLine !== generated.line) { + currentLineDiff = 0; + } + currentLine = generated.line; + generated.column += currentLineDiff; + if (patch && m.generatedLine - 1 === patch.span.end.line && m.generatedColumn === patch.span.end.character) { + var originalLength = patch.span.end.character - patch.span.start.character; + var modifiedLength = patch.content.length; + var lengthDiff = modifiedLength - originalLength; + currentLineDiff += lengthDiff; + generated.column += lengthDiff; + patches.pop(); + } + source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; + source = source.replace(/\\/g, '/'); + smg.addMapping({ source: source, name: m.name, original: original, generated: generated }); + }, null, sm.SourceMapConsumer.GENERATED_ORDER); + if (source) { + smg.setSourceContent(source, smc.sourceContentFor(source)); + } + return JSON.parse(smg.toString()); + } + nls_1.patchSourcemap = patchSourcemap; + function patch(moduleId, typescript, javascript, sourcemap) { + var _a = analyze(typescript), localizeCalls = _a.localizeCalls, nlsExpressions = _a.nlsExpressions; + if (localizeCalls.length === 0) { + return { javascript: javascript, sourcemap: sourcemap }; + } + var nlsKeys = template(localizeCalls.map(function (lc) { return lc.key; })); + var nls = template(localizeCalls.map(function (lc) { return lc.value; })); + var smc = new sm.SourceMapConsumer(sourcemap); + var positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); + var i = 0; + // build patches + var patches = lazy(localizeCalls) + .map(function (lc) { return ([ + { range: lc.keySpan, content: '' + (i++) }, + { range: lc.valueSpan, content: 'null' } + ]); }) + .flatten() + .map(function (c) { + var start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start))); + var end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); + return { span: { start: start, end: end }, content: c.content }; + }) + .toArray(); + javascript = patchJavascript(patches, javascript, moduleId); + // since imports are not within the sourcemap information, + // we must do this MacGyver style + if (nlsExpressions.length) { + javascript = javascript.replace(/^define\(.*$/m, function (line) { + return line.replace(/(['"])vs\/nls\1/g, "$1vs/nls!" + moduleId + "$1"); + }); + } + sourcemap = patchSourcemap(patches, sourcemap, smc); + return { javascript: javascript, sourcemap: sourcemap, nlsKeys: nlsKeys, nls: nls }; + } + nls_1.patch = patch; + function patchFiles(javascriptFile, typescript) { + // hack? + var moduleId = javascriptFile.relative + .replace(/\.js$/, '') + .replace(/\\/g, '/'); + var _a = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap), javascript = _a.javascript, sourcemap = _a.sourcemap, nlsKeys = _a.nlsKeys, nls = _a.nls; + var result = [fileFrom(javascriptFile, javascript)]; + result[0].sourceMap = sourcemap; + if (nlsKeys) { + result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); + } + if (nls) { + result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); + } + return result; + } + nls_1.patchFiles = patchFiles; +})(nls || (nls = {})); +module.exports = nls; diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 1ba3c292f83..91c7658e7de 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -1,230 +1,229 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -var path = require("path"); -var gulp = require("gulp"); -var sourcemaps = require("gulp-sourcemaps"); -var filter = require("gulp-filter"); -var minifyCSS = require("gulp-cssnano"); -var uglify = require("gulp-uglify"); -var es = require("event-stream"); -var concat = require("gulp-concat"); -var VinylFile = require("vinyl"); -var bundle = require("./bundle"); -var util = require("./util"); -var i18n = require("./i18n"); -var gulpUtil = require("gulp-util"); -var flatmap = require("gulp-flatmap"); -var pump = require("pump"); -var REPO_ROOT_PATH = path.join(__dirname, '../..'); -function log(prefix, message) { - gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); -} -function loaderConfig(emptyPaths) { - var result = { - paths: { - 'vs': 'out-build/vs', - 'vscode': 'empty:' - }, - nodeModules: emptyPaths || [] - }; - result['vs/css'] = { inlineResources: true }; - return result; -} -exports.loaderConfig = loaderConfig; -var IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(bundledFileHeader, bundleLoader) { - var sources = [ - 'out-build/vs/loader.js' - ]; - if (bundleLoader) { - sources = sources.concat([ - 'out-build/vs/css.js', - 'out-build/vs/nls.js' - ]); - } - var isFirst = true; - return (gulp - .src(sources, { base: 'out-build' }) - .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '', - contents: new Buffer(bundledFileHeader) - })); - this.emit('data', data); - } - else { - this.emit('data', data); - } - })) - .pipe(util.loadSourcemaps()) - .pipe(concat('vs/loader.js')) - .pipe(es.mapSync(function (f) { - f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); - return f; - }))); -} -function toConcatStream(bundledFileHeader, sources, dest) { - var useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); - // If a bundle ends up including in any of the sources our copyright, then - // insert a fake source at the beginning of each bundle with our copyright - var containsOurCopyright = false; - for (var i = 0, len = sources.length; i < len; i++) { - var fileContents = sources[i].contents; - if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { - containsOurCopyright = true; - break; - } - } - if (containsOurCopyright) { - sources.unshift({ - path: null, - contents: bundledFileHeader - }); - } - var treatedSources = sources.map(function (source) { - var root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - var base = source.path ? root + '/out-build' : ''; - return new VinylFile({ - path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', - base: base, - contents: new Buffer(source.contents) - }); - }); - return es.readArray(treatedSources) - .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) - .pipe(concat(dest)); -} -function toBundleStream(bundledFileHeader, bundles) { - return es.merge(bundles.map(function (bundle) { - return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); - })); -} -function optimizeTask(opts) { - var entryPoints = opts.entryPoints; - var otherSources = opts.otherSources; - var resources = opts.resources; - var loaderConfig = opts.loaderConfig; - var bundledFileHeader = opts.header; - var bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); - var out = opts.out; - return function () { - var bundlesStream = es.through(); // this stream will contain the bundled files - var resourcesStream = es.through(); // this stream will contain the resources - var bundleInfoStream = es.through(); // this stream will contain bundleInfo.json - bundle.bundle(entryPoints, loaderConfig, function (err, result) { - if (err) { - return bundlesStream.emit('error', JSON.stringify(err)); - } - toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); - // Remove css inlined resources - var filteredResources = resources.slice(); - result.cssInlinedResources.forEach(function (resource) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log('optimizer', 'excluding inlined: ' + resource); - } - filteredResources.push('!' + resource); - }); - gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); - var bundleInfoArray = []; - if (opts.bundleInfo) { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: new Buffer(JSON.stringify(result.bundleData, null, '\t')) - })); - } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); - }); - var otherSourcesStream = es.through(); - var otherSourcesStreamArr = []; - gulp.src(otherSources, { base: 'out-build' }) - .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); - }, function () { - if (!otherSourcesStreamArr.length) { - setTimeout(function () { otherSourcesStream.emit('end'); }, 0); - } - else { - es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); - } - })); - var result = es.merge(loader(bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); - return result - .pipe(sourcemaps.write('./', { - sourceRoot: null, - addComment: true, - includeContent: true - })) - .pipe(i18n.processNlsFiles({ - fileHeader: bundledFileHeader - })) - .pipe(gulp.dest(out)); - }; -} -exports.optimizeTask = optimizeTask; -; -/** - * Wrap around uglify and allow the preserveComments function - * to have a file "context" to include our copyright only once per file. - */ -function uglifyWithCopyrights() { - var preserveComments = function (f) { - return function (node, comment) { - var text = comment.value; - var type = comment.type; - if (/@minifier_do_not_preserve/.test(text)) { - return false; - } - var isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text); - if (isOurCopyright) { - if (f.__hasOurCopyright) { - return false; - } - f.__hasOurCopyright = true; - return true; - } - if ('comment2' === type) { - // check for /*!. Note that text doesn't contain leading /* - return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text); - } - else if ('comment1' === type) { - return /license|copyright/i.test(text); - } - return false; - }; - }; - var input = es.through(); - var output = input - .pipe(flatmap(function (stream, f) { - return stream - .pipe(uglify({ preserveComments: preserveComments(f) })); - })); - return es.duplex(input, output); -} -function minifyTask(src, sourceMapBaseUrl) { - var sourceMappingURL = sourceMapBaseUrl && (function (f) { return sourceMapBaseUrl + "/" + f.relative + ".map"; }); - return function (cb) { - var jsFilter = filter('**/*.js', { restore: true }); - var cssFilter = filter('**/*.css', { restore: true }); - pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.write('./', { - sourceMappingURL: sourceMappingURL, - sourceRoot: null, - includeContent: true, - addComment: true - }), gulp.dest(src + '-min'), function (err) { - if (err instanceof uglify.GulpUglifyError) { - console.error("Uglify error in '" + (err.cause && err.cause.filename) + "'"); - } - cb(err); - }); - }; -} -exports.minifyTask = minifyTask; -; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var path = require("path"); +var gulp = require("gulp"); +var sourcemaps = require("gulp-sourcemaps"); +var filter = require("gulp-filter"); +var minifyCSS = require("gulp-cssnano"); +var uglify = require("gulp-uglify"); +var es = require("event-stream"); +var concat = require("gulp-concat"); +var VinylFile = require("vinyl"); +var bundle = require("./bundle"); +var util = require("./util"); +var i18n = require("./i18n"); +var gulpUtil = require("gulp-util"); +var flatmap = require("gulp-flatmap"); +var pump = require("pump"); +var REPO_ROOT_PATH = path.join(__dirname, '../..'); +function log(prefix, message) { + gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); +} +function loaderConfig(emptyPaths) { + var result = { + paths: { + 'vs': 'out-build/vs', + 'vscode': 'empty:' + }, + nodeModules: emptyPaths || [] + }; + result['vs/css'] = { inlineResources: true }; + return result; +} +exports.loaderConfig = loaderConfig; +var IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; +function loader(bundledFileHeader, bundleLoader) { + var sources = [ + 'out-build/vs/loader.js' + ]; + if (bundleLoader) { + sources = sources.concat([ + 'out-build/vs/css.js', + 'out-build/vs/nls.js' + ]); + } + var isFirst = true; + return (gulp + .src(sources, { base: 'out-build' }) + .pipe(es.through(function (data) { + if (isFirst) { + isFirst = false; + this.emit('data', new VinylFile({ + path: 'fake', + base: '', + contents: new Buffer(bundledFileHeader) + })); + this.emit('data', data); + } + else { + this.emit('data', data); + } + })) + .pipe(util.loadSourcemaps()) + .pipe(concat('vs/loader.js')) + .pipe(es.mapSync(function (f) { + f.sourceMap.sourceRoot = util.toFileUri(path.join(REPO_ROOT_PATH, 'src')); + return f; + }))); +} +function toConcatStream(bundledFileHeader, sources, dest) { + var useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); + // If a bundle ends up including in any of the sources our copyright, then + // insert a fake source at the beginning of each bundle with our copyright + var containsOurCopyright = false; + for (var i = 0, len = sources.length; i < len; i++) { + var fileContents = sources[i].contents; + if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { + containsOurCopyright = true; + break; + } + } + if (containsOurCopyright) { + sources.unshift({ + path: null, + contents: bundledFileHeader + }); + } + var treatedSources = sources.map(function (source) { + var root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; + var base = source.path ? root + '/out-build' : ''; + return new VinylFile({ + path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', + base: base, + contents: new Buffer(source.contents) + }); + }); + return es.readArray(treatedSources) + .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) + .pipe(concat(dest)); +} +function toBundleStream(bundledFileHeader, bundles) { + return es.merge(bundles.map(function (bundle) { + return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + })); +} +function optimizeTask(opts) { + var entryPoints = opts.entryPoints; + var otherSources = opts.otherSources; + var resources = opts.resources; + var loaderConfig = opts.loaderConfig; + var bundledFileHeader = opts.header; + var bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); + var out = opts.out; + return function () { + var bundlesStream = es.through(); // this stream will contain the bundled files + var resourcesStream = es.through(); // this stream will contain the resources + var bundleInfoStream = es.through(); // this stream will contain bundleInfo.json + bundle.bundle(entryPoints, loaderConfig, function (err, result) { + if (err) { + return bundlesStream.emit('error', JSON.stringify(err)); + } + toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + // Remove css inlined resources + var filteredResources = resources.slice(); + result.cssInlinedResources.forEach(function (resource) { + if (process.env['VSCODE_BUILD_VERBOSE']) { + log('optimizer', 'excluding inlined: ' + resource); + } + filteredResources.push('!' + resource); + }); + gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + var bundleInfoArray = []; + if (opts.bundleInfo) { + bundleInfoArray.push(new VinylFile({ + path: 'bundleInfo.json', + base: '.', + contents: new Buffer(JSON.stringify(result.bundleData, null, '\t')) + })); + } + es.readArray(bundleInfoArray).pipe(bundleInfoStream); + }); + var otherSourcesStream = es.through(); + var otherSourcesStreamArr = []; + gulp.src(otherSources, { base: 'out-build' }) + .pipe(es.through(function (data) { + otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + }, function () { + if (!otherSourcesStreamArr.length) { + setTimeout(function () { otherSourcesStream.emit('end'); }, 0); + } + else { + es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); + } + })); + var result = es.merge(loader(bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); + return result + .pipe(sourcemaps.write('./', { + sourceRoot: null, + addComment: true, + includeContent: true + })) + .pipe(i18n.processNlsFiles({ + fileHeader: bundledFileHeader + })) + .pipe(gulp.dest(out)); + }; +} +exports.optimizeTask = optimizeTask; +; +/** + * Wrap around uglify and allow the preserveComments function + * to have a file "context" to include our copyright only once per file. + */ +function uglifyWithCopyrights() { + var preserveComments = function (f) { + return function (node, comment) { + var text = comment.value; + var type = comment.type; + if (/@minifier_do_not_preserve/.test(text)) { + return false; + } + var isOurCopyright = IS_OUR_COPYRIGHT_REGEXP.test(text); + if (isOurCopyright) { + if (f.__hasOurCopyright) { + return false; + } + f.__hasOurCopyright = true; + return true; + } + if ('comment2' === type) { + // check for /*!. Note that text doesn't contain leading /* + return (text.length > 0 && text[0] === '!') || /@preserve|license|@cc_on|copyright/i.test(text); + } + else if ('comment1' === type) { + return /license|copyright/i.test(text); + } + return false; + }; + }; + var input = es.through(); + var output = input + .pipe(flatmap(function (stream, f) { + return stream + .pipe(uglify({ preserveComments: preserveComments(f) })); + })); + return es.duplex(input, output); +} +function minifyTask(src, sourceMapBaseUrl) { + var sourceMappingURL = sourceMapBaseUrl && (function (f) { return sourceMapBaseUrl + "/" + f.relative + ".map"; }); + return function (cb) { + var jsFilter = filter('**/*.js', { restore: true }); + var cssFilter = filter('**/*.css', { restore: true }); + pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.write('./', { + sourceMappingURL: sourceMappingURL, + sourceRoot: null, + includeContent: true, + addComment: true + }), gulp.dest(src + '-min'), function (err) { + if (err instanceof uglify.GulpUglifyError) { + console.error("Uglify error in '" + (err.cause && err.cause.filename) + "'"); + } + cb(err); + }); + }; +} +exports.minifyTask = minifyTask; +; diff --git a/build/lib/reporter.js b/build/lib/reporter.js index aad8a561545..bccc1a88ac1 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -1,83 +1,80 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -var es = require("event-stream"); -var _ = require("underscore"); -var util = require("gulp-util"); -var fs = require("fs"); -var path = require("path"); -var allErrors = []; -var startTime = null; -var count = 0; -function onStart() { - if (count++ > 0) { - return; - } - startTime = new Date().getTime(); - util.log("Starting " + util.colors.green('compilation') + "..."); -} -function onEnd() { - if (--count > 0) { - return; - } - log(); -} -var buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); -try { - fs.mkdirSync(path.dirname(buildLogPath)); -} -catch (err) { - // ignore -} -function log() { - var errors = _.flatten(allErrors); - errors.map(function (err) { return util.log(util.colors.red('Error') + ": " + err); }); - var regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; - var messages = errors - .map(function (err) { return regex.exec(err); }) - .filter(function (match) { return !!match; }) - .map(function (_a) { - var path = _a[1], line = _a[2], column = _a[3], message = _a[4]; - return ({ path: path, line: Number.parseInt(line), column: Number.parseInt(column), message: message }); - }); - try { - fs.writeFileSync(buildLogPath, JSON.stringify(messages)); - } - catch (err) { - //noop - } - util.log("Finished " + util.colors.green('compilation') + " with " + errors.length + " errors after " + util.colors.magenta((new Date().getTime() - startTime) + ' ms')); -} -function createReporter() { - var errors = []; - allErrors.push(errors); - var ReportFunc = (function () { - function ReportFunc(err) { - errors.push(err); - } - ReportFunc.hasErrors = function () { - return errors.length > 0; - }; - ReportFunc.end = function (emitError) { - errors.length = 0; - onStart(); - return es.through(null, function () { - onEnd(); - if (emitError && errors.length > 0) { - log(); - this.emit('error'); - } - else { - this.emit('end'); - } - }); - }; - return ReportFunc; - }()); - return ReportFunc; -} -exports.createReporter = createReporter; -; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var es = require("event-stream"); +var _ = require("underscore"); +var util = require("gulp-util"); +var fs = require("fs"); +var path = require("path"); +var allErrors = []; +var startTime = null; +var count = 0; +function onStart() { + if (count++ > 0) { + return; + } + startTime = new Date().getTime(); + util.log("Starting " + util.colors.green('compilation') + "..."); +} +function onEnd() { + if (--count > 0) { + return; + } + log(); +} +var buildLogPath = path.join(path.dirname(path.dirname(__dirname)), '.build', 'log'); +try { + fs.mkdirSync(path.dirname(buildLogPath)); +} +catch (err) { +} +function log() { + var errors = _.flatten(allErrors); + errors.map(function (err) { return util.log(util.colors.red('Error') + ": " + err); }); + var regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; + var messages = errors + .map(function (err) { return regex.exec(err); }) + .filter(function (match) { return !!match; }) + .map(function (_a) { + var path = _a[1], line = _a[2], column = _a[3], message = _a[4]; + return ({ path: path, line: Number.parseInt(line), column: Number.parseInt(column), message: message }); + }); + try { + fs.writeFileSync(buildLogPath, JSON.stringify(messages)); + } + catch (err) { + } + util.log("Finished " + util.colors.green('compilation') + " with " + errors.length + " errors after " + util.colors.magenta((new Date().getTime() - startTime) + ' ms')); +} +function createReporter() { + var errors = []; + allErrors.push(errors); + var ReportFunc = (function () { + function ReportFunc(err) { + errors.push(err); + } + ReportFunc.hasErrors = function () { + return errors.length > 0; + }; + ReportFunc.end = function (emitError) { + errors.length = 0; + onStart(); + return es.through(null, function () { + onEnd(); + if (emitError && errors.length > 0) { + log(); + this.emit('error'); + } + else { + this.emit('end'); + } + }); + }; + return ReportFunc; + }()); + return ReportFunc; +} +exports.createReporter = createReporter; +; diff --git a/build/lib/tslint/allowAsyncRule.js b/build/lib/tslint/allowAsyncRule.js new file mode 100644 index 00000000000..1e4943f795a --- /dev/null +++ b/build/lib/tslint/allowAsyncRule.js @@ -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"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var ts = require("typescript"); +var Lint = require("tslint"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var allowed = this.getOptions().ruleArguments[0]; + return this.applyWithWalker(new AsyncRuleWalker(sourceFile, this.getOptions(), allowed)); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var AsyncRuleWalker = (function (_super) { + __extends(AsyncRuleWalker, _super); + function AsyncRuleWalker(file, opts, allowed) { + var _this = _super.call(this, file, opts) || this; + _this.allowed = allowed; + return _this; + } + AsyncRuleWalker.prototype.visitMethodDeclaration = function (node) { + this.visitFunctionLikeDeclaration(node); + }; + AsyncRuleWalker.prototype.visitFunctionDeclaration = function (node) { + this.visitFunctionLikeDeclaration(node); + }; + AsyncRuleWalker.prototype.visitFunctionLikeDeclaration = function (node) { + var _this = this; + var flags = ts.getCombinedModifierFlags(node); + if (!(flags & ts.ModifierFlags.Async)) { + return; + } + var path = node.getSourceFile().path; + var pathParts = path.split(/\\|\//); + if (pathParts.some(function (part) { return _this.allowed.some(function (allowed) { return part === allowed; }); })) { + return; + } + var message = "You are not allowed to use async function in this layer. Allowed layers are: [" + this.allowed + "]"; + this.addFailureAtNode(node, message); + }; + return AsyncRuleWalker; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/allowAsyncRule.ts b/build/lib/tslint/allowAsyncRule.ts new file mode 100644 index 00000000000..6506929cb9b --- /dev/null +++ b/build/lib/tslint/allowAsyncRule.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const allowed = this.getOptions().ruleArguments[0] as string[]; + return this.applyWithWalker(new AsyncRuleWalker(sourceFile, this.getOptions(), allowed)); + } +} + +class AsyncRuleWalker extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, opts: Lint.IOptions, private allowed: string[]) { + super(file, opts); + } + + protected visitMethodDeclaration(node: ts.MethodDeclaration): void { + this.visitFunctionLikeDeclaration(node); + } + + protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void { + this.visitFunctionLikeDeclaration(node); + } + + private visitFunctionLikeDeclaration(node: ts.FunctionLikeDeclaration) { + const flags = ts.getCombinedModifierFlags(node); + + if (!(flags & ts.ModifierFlags.Async)) { + return; + } + + const path = node.getSourceFile().path; + const pathParts = path.split(/\\|\//); + + if (pathParts.some(part => this.allowed.some(allowed => part === allowed))) { + return; + } + + const message = `You are not allowed to use async function in this layer. Allowed layers are: [${this.allowed}]`; + this.addFailureAtNode(node, message); + } +} diff --git a/build/lib/tslint/duplicateImportsRule.js b/build/lib/tslint/duplicateImportsRule.js index e6a5cb6a545..9b4164a0dc2 100644 --- a/build/lib/tslint/duplicateImportsRule.js +++ b/build/lib/tslint/duplicateImportsRule.js @@ -1,50 +1,44 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var path_1 = require("path"); -var Lint = require("tslint"); -var Rule = (function (_super) { - __extends(Rule, _super); - function Rule() { - return _super !== null && _super.apply(this, arguments) || this; - } - Rule.prototype.apply = function (sourceFile) { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); - }; - return Rule; -}(Lint.Rules.AbstractRule)); -exports.Rule = Rule; -var ImportPatterns = (function (_super) { - __extends(ImportPatterns, _super); - function ImportPatterns(file, opts) { - var _this = _super.call(this, file, opts) || this; - _this.imports = Object.create(null); - return _this; - } - ImportPatterns.prototype.visitImportDeclaration = function (node) { - var path = node.moduleSpecifier.getText(); - // remove quotes - path = path.slice(1, -1); - if (path[0] === '.') { - path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); - } - if (this.imports[path]) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Duplicate imports for '" + path + "'.")); - } - this.imports[path] = true; - }; - return ImportPatterns; -}(Lint.RuleWalker)); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var path_1 = require("path"); +var Lint = require("tslint"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var ImportPatterns = (function (_super) { + __extends(ImportPatterns, _super); + function ImportPatterns(file, opts) { + var _this = _super.call(this, file, opts) || this; + _this.imports = Object.create(null); + return _this; + } + ImportPatterns.prototype.visitImportDeclaration = function (node) { + var path = node.moduleSpecifier.getText(); + // remove quotes + path = path.slice(1, -1); + if (path[0] === '.') { + path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); + } + if (this.imports[path]) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Duplicate imports for '" + path + "'.")); + } + this.imports[path] = true; + }; + return ImportPatterns; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/importPatternsRule.js b/build/lib/tslint/importPatternsRule.js index 3173bc9d559..b266dc4ba19 100644 --- a/build/lib/tslint/importPatternsRule.js +++ b/build/lib/tslint/importPatternsRule.js @@ -1,57 +1,51 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Lint = require("tslint"); -var minimatch = require("minimatch"); -var Rule = (function (_super) { - __extends(Rule, _super); - function Rule() { - return _super !== null && _super.apply(this, arguments) || this; - } - Rule.prototype.apply = function (sourceFile) { - var configs = this.getOptions().ruleArguments; - for (var _i = 0, configs_1 = configs; _i < configs_1.length; _i++) { - var config = configs_1[_i]; - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); - } - } - return []; - }; - return Rule; -}(Lint.Rules.AbstractRule)); -exports.Rule = Rule; -var ImportPatterns = (function (_super) { - __extends(ImportPatterns, _super); - function ImportPatterns(file, opts, _config) { - var _this = _super.call(this, file, opts) || this; - _this._config = _config; - return _this; - } - ImportPatterns.prototype.visitImportDeclaration = function (node) { - var path = node.moduleSpecifier.getText(); - // remove quotes - path = path.slice(1, -1); - // ignore relative paths - if (path[0] === '.') { - return; - } - if (!minimatch(path, this._config.restrictions)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Imports violates '" + this._config.restrictions + "'-restriction.")); - } - }; - return ImportPatterns; -}(Lint.RuleWalker)); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Lint = require("tslint"); +var minimatch = require("minimatch"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var configs = this.getOptions().ruleArguments; + for (var _i = 0, configs_1 = configs; _i < configs_1.length; _i++) { + var config = configs_1[_i]; + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); + } + } + return []; + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var ImportPatterns = (function (_super) { + __extends(ImportPatterns, _super); + function ImportPatterns(file, opts, _config) { + var _this = _super.call(this, file, opts) || this; + _this._config = _config; + return _this; + } + ImportPatterns.prototype.visitImportDeclaration = function (node) { + var path = node.moduleSpecifier.getText(); + // remove quotes + path = path.slice(1, -1); + // ignore relative paths + if (path[0] === '.') { + return; + } + if (!minimatch(path, this._config.restrictions)) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Imports violates '" + this._config.restrictions + "'-restriction.")); + } + }; + return ImportPatterns; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/layeringRule.js b/build/lib/tslint/layeringRule.js index 5aba40975a9..b5fdadd7daa 100644 --- a/build/lib/tslint/layeringRule.js +++ b/build/lib/tslint/layeringRule.js @@ -1,85 +1,79 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Lint = require("tslint"); -var path_1 = require("path"); -var Rule = (function (_super) { - __extends(Rule, _super); - function Rule() { - return _super !== null && _super.apply(this, arguments) || this; - } - Rule.prototype.apply = function (sourceFile) { - var parts = path_1.dirname(sourceFile.fileName).split(/\\|\//); - var ruleArgs = this.getOptions().ruleArguments[0]; - var config; - for (var i = parts.length - 1; i >= 0; i--) { - if (ruleArgs[parts[i]]) { - config = { - allowed: new Set(ruleArgs[parts[i]]).add(parts[i]), - disallowed: new Set() - }; - Object.keys(ruleArgs).forEach(function (key) { - if (!config.allowed.has(key)) { - config.disallowed.add(key); - } - }); - break; - } - } - if (!config) { - return []; - } - return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); - }; - return Rule; -}(Lint.Rules.AbstractRule)); -exports.Rule = Rule; -var LayeringRule = (function (_super) { - __extends(LayeringRule, _super); - function LayeringRule(file, config, opts) { - var _this = _super.call(this, file, opts) || this; - _this._config = config; - return _this; - } - LayeringRule.prototype.visitImportDeclaration = function (node) { - var path = node.moduleSpecifier.getText(); - // remove quotes - path = path.slice(1, -1); - if (path[0] === '.') { - path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); - } - var parts = path_1.dirname(path).split(/\\|\//); - for (var i = parts.length - 1; i >= 0; i--) { - var part = parts[i]; - if (this._config.allowed.has(part)) { - // GOOD - same layer - return; - } - if (this._config.disallowed.has(part)) { - // BAD - wrong layer - var message = "Bad layering. You are not allowed to access '" + part + "' from here, allowed layers are: [" + LayeringRule._print(this._config.allowed) + "]"; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); - return; - } - } - }; - LayeringRule._print = function (set) { - var r = []; - set.forEach(function (e) { return r.push(e); }); - return r.join(', '); - }; - return LayeringRule; -}(Lint.RuleWalker)); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Lint = require("tslint"); +var path_1 = require("path"); +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + var parts = path_1.dirname(sourceFile.fileName).split(/\\|\//); + var ruleArgs = this.getOptions().ruleArguments[0]; + var config; + for (var i = parts.length - 1; i >= 0; i--) { + if (ruleArgs[parts[i]]) { + config = { + allowed: new Set(ruleArgs[parts[i]]).add(parts[i]), + disallowed: new Set() + }; + Object.keys(ruleArgs).forEach(function (key) { + if (!config.allowed.has(key)) { + config.disallowed.add(key); + } + }); + break; + } + } + if (!config) { + return []; + } + return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +var LayeringRule = (function (_super) { + __extends(LayeringRule, _super); + function LayeringRule(file, config, opts) { + var _this = _super.call(this, file, opts) || this; + _this._config = config; + return _this; + } + LayeringRule.prototype.visitImportDeclaration = function (node) { + var path = node.moduleSpecifier.getText(); + // remove quotes + path = path.slice(1, -1); + if (path[0] === '.') { + path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); + } + var parts = path_1.dirname(path).split(/\\|\//); + for (var i = parts.length - 1; i >= 0; i--) { + var part = parts[i]; + if (this._config.allowed.has(part)) { + // GOOD - same layer + return; + } + if (this._config.disallowed.has(part)) { + // BAD - wrong layer + var message = "Bad layering. You are not allowed to access '" + part + "' from here, allowed layers are: [" + LayeringRule._print(this._config.allowed) + "]"; + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); + return; + } + } + }; + LayeringRule._print = function (set) { + var r = []; + set.forEach(function (e) { return r.push(e); }); + return r.join(', '); + }; + return LayeringRule; +}(Lint.RuleWalker)); diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js index 93305f70b3a..8da45aa86d3 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.js +++ b/build/lib/tslint/noUnexternalizedStringsRule.js @@ -1,177 +1,171 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var ts = require("typescript"); -var Lint = require("tslint"); -/** - * Implementation of the no-unexternalized-strings rule. - */ -var Rule = (function (_super) { - __extends(Rule, _super); - function Rule() { - return _super !== null && _super.apply(this, arguments) || this; - } - Rule.prototype.apply = function (sourceFile) { - return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); - }; - return Rule; -}(Lint.Rules.AbstractRule)); -exports.Rule = Rule; -function isStringLiteral(node) { - return node && node.kind === ts.SyntaxKind.StringLiteral; -} -function isObjectLiteral(node) { - return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; -} -function isPropertyAssignment(node) { - return node && node.kind === ts.SyntaxKind.PropertyAssignment; -} -var NoUnexternalizedStringsRuleWalker = (function (_super) { - __extends(NoUnexternalizedStringsRuleWalker, _super); - function NoUnexternalizedStringsRuleWalker(file, opts) { - var _this = _super.call(this, file, opts) || this; - _this.signatures = Object.create(null); - _this.ignores = Object.create(null); - _this.messageIndex = undefined; - _this.keyIndex = undefined; - _this.usedKeys = Object.create(null); - var options = _this.getOptions(); - var first = options && options.length > 0 ? options[0] : null; - if (first) { - if (Array.isArray(first.signatures)) { - first.signatures.forEach(function (signature) { return _this.signatures[signature] = true; }); - } - if (Array.isArray(first.ignores)) { - first.ignores.forEach(function (ignore) { return _this.ignores[ignore] = true; }); - } - if (typeof first.messageIndex !== 'undefined') { - _this.messageIndex = first.messageIndex; - } - if (typeof first.keyIndex !== 'undefined') { - _this.keyIndex = first.keyIndex; - } - } - return _this; - } - NoUnexternalizedStringsRuleWalker.prototype.visitSourceFile = function (node) { - var _this = this; - _super.prototype.visitSourceFile.call(this, node); - Object.keys(this.usedKeys).forEach(function (key) { - var occurences = _this.usedKeys[key]; - if (occurences.length > 1) { - occurences.forEach(function (occurence) { - _this.addFailure((_this.createFailure(occurence.key.getStart(), occurence.key.getWidth(), "Duplicate key " + occurence.key.getText() + " with different message value."))); - }); - } - }); - }; - NoUnexternalizedStringsRuleWalker.prototype.visitStringLiteral = function (node) { - this.checkStringLiteral(node); - _super.prototype.visitStringLiteral.call(this, node); - }; - NoUnexternalizedStringsRuleWalker.prototype.checkStringLiteral = function (node) { - var text = node.getText(); - var doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; - var info = this.findDescribingParent(node); - // Ignore strings in import and export nodes. - if (info && info.ignoreUsage) { - return; - } - var callInfo = info ? info.callInfo : null; - var functionName = callInfo ? callInfo.callExpression.expression.getText() : null; - if (functionName && this.ignores[functionName]) { - return; - } - if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) { - var s = node.getText(); - var replacement = new Lint.Replacement(node.getStart(), node.getWidth(), "nls.localize('KEY-" + s.substring(1, s.length - 1) + "', " + s + ")"); - var fix = new Lint.Fix('Unexternalitzed string', [replacement]); - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Unexternalized string found: " + node.getText(), fix)); - return; - } - // We have a single quoted string outside a localize function name. - if (!doubleQuoted && !this.signatures[functionName]) { - return; - } - // We have a string that is a direct argument into the localize call. - var keyArg = callInfo.argIndex === this.keyIndex - ? callInfo.callExpression.arguments[this.keyIndex] - : null; - if (keyArg) { - if (isStringLiteral(keyArg)) { - this.recordKey(keyArg, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } - else if (isObjectLiteral(keyArg)) { - for (var i = 0; i < keyArg.properties.length; i++) { - var property = keyArg.properties[i]; - if (isPropertyAssignment(property)) { - var name_1 = property.name.getText(); - if (name_1 === 'key') { - var initializer = property.initializer; - if (isStringLiteral(initializer)) { - this.recordKey(initializer, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } - break; - } - } - } - } - } - var messageArg = callInfo.argIndex === this.messageIndex - ? callInfo.callExpression.arguments[this.messageIndex] - : null; - if (messageArg && messageArg !== node) { - this.addFailure(this.createFailure(messageArg.getStart(), messageArg.getWidth(), "Message argument to '" + callInfo.callExpression.expression.getText() + "' must be a string literal.")); - return; - } - }; - NoUnexternalizedStringsRuleWalker.prototype.recordKey = function (keyNode, messageNode) { - var text = keyNode.getText(); - var occurences = this.usedKeys[text]; - if (!occurences) { - occurences = []; - this.usedKeys[text] = occurences; - } - if (messageNode) { - if (occurences.some(function (pair) { return pair.message ? pair.message.getText() === messageNode.getText() : false; })) { - return; - } - } - occurences.push({ key: keyNode, message: messageNode }); - }; - NoUnexternalizedStringsRuleWalker.prototype.findDescribingParent = function (node) { - var parent; - while ((parent = node.parent)) { - var kind = parent.kind; - if (kind === ts.SyntaxKind.CallExpression) { - var callExpression = parent; - return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; - } - else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { - return { ignoreUsage: true }; - } - else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration - || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration - || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration - || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { - return null; - } - node = parent; - } - }; - return NoUnexternalizedStringsRuleWalker; -}(Lint.RuleWalker)); -NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"'; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var ts = require("typescript"); +var Lint = require("tslint"); +/** + * Implementation of the no-unexternalized-strings rule. + */ +var Rule = (function (_super) { + __extends(Rule, _super); + function Rule() { + return _super !== null && _super.apply(this, arguments) || this; + } + Rule.prototype.apply = function (sourceFile) { + return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); + }; + return Rule; +}(Lint.Rules.AbstractRule)); +exports.Rule = Rule; +function isStringLiteral(node) { + return node && node.kind === ts.SyntaxKind.StringLiteral; +} +function isObjectLiteral(node) { + return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; +} +function isPropertyAssignment(node) { + return node && node.kind === ts.SyntaxKind.PropertyAssignment; +} +var NoUnexternalizedStringsRuleWalker = (function (_super) { + __extends(NoUnexternalizedStringsRuleWalker, _super); + function NoUnexternalizedStringsRuleWalker(file, opts) { + var _this = _super.call(this, file, opts) || this; + _this.signatures = Object.create(null); + _this.ignores = Object.create(null); + _this.messageIndex = undefined; + _this.keyIndex = undefined; + _this.usedKeys = Object.create(null); + var options = _this.getOptions(); + var first = options && options.length > 0 ? options[0] : null; + if (first) { + if (Array.isArray(first.signatures)) { + first.signatures.forEach(function (signature) { return _this.signatures[signature] = true; }); + } + if (Array.isArray(first.ignores)) { + first.ignores.forEach(function (ignore) { return _this.ignores[ignore] = true; }); + } + if (typeof first.messageIndex !== 'undefined') { + _this.messageIndex = first.messageIndex; + } + if (typeof first.keyIndex !== 'undefined') { + _this.keyIndex = first.keyIndex; + } + } + return _this; + } + NoUnexternalizedStringsRuleWalker.prototype.visitSourceFile = function (node) { + var _this = this; + _super.prototype.visitSourceFile.call(this, node); + Object.keys(this.usedKeys).forEach(function (key) { + var occurences = _this.usedKeys[key]; + if (occurences.length > 1) { + occurences.forEach(function (occurence) { + _this.addFailure((_this.createFailure(occurence.key.getStart(), occurence.key.getWidth(), "Duplicate key " + occurence.key.getText() + " with different message value."))); + }); + } + }); + }; + NoUnexternalizedStringsRuleWalker.prototype.visitStringLiteral = function (node) { + this.checkStringLiteral(node); + _super.prototype.visitStringLiteral.call(this, node); + }; + NoUnexternalizedStringsRuleWalker.prototype.checkStringLiteral = function (node) { + var text = node.getText(); + var doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; + var info = this.findDescribingParent(node); + // Ignore strings in import and export nodes. + if (info && info.ignoreUsage) { + return; + } + var callInfo = info ? info.callInfo : null; + var functionName = callInfo ? callInfo.callExpression.expression.getText() : null; + if (functionName && this.ignores[functionName]) { + return; + } + if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) { + var s = node.getText(); + var replacement = new Lint.Replacement(node.getStart(), node.getWidth(), "nls.localize('KEY-" + s.substring(1, s.length - 1) + "', " + s + ")"); + var fix = new Lint.Fix('Unexternalitzed string', [replacement]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), "Unexternalized string found: " + node.getText(), fix)); + return; + } + // We have a single quoted string outside a localize function name. + if (!doubleQuoted && !this.signatures[functionName]) { + return; + } + // We have a string that is a direct argument into the localize call. + var keyArg = callInfo.argIndex === this.keyIndex + ? callInfo.callExpression.arguments[this.keyIndex] + : null; + if (keyArg) { + if (isStringLiteral(keyArg)) { + this.recordKey(keyArg, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } + else if (isObjectLiteral(keyArg)) { + for (var i = 0; i < keyArg.properties.length; i++) { + var property = keyArg.properties[i]; + if (isPropertyAssignment(property)) { + var name_1 = property.name.getText(); + if (name_1 === 'key') { + var initializer = property.initializer; + if (isStringLiteral(initializer)) { + this.recordKey(initializer, this.messageIndex ? callInfo.callExpression.arguments[this.messageIndex] : undefined); + } + break; + } + } + } + } + } + var messageArg = callInfo.argIndex === this.messageIndex + ? callInfo.callExpression.arguments[this.messageIndex] + : null; + if (messageArg && messageArg !== node) { + this.addFailure(this.createFailure(messageArg.getStart(), messageArg.getWidth(), "Message argument to '" + callInfo.callExpression.expression.getText() + "' must be a string literal.")); + return; + } + }; + NoUnexternalizedStringsRuleWalker.prototype.recordKey = function (keyNode, messageNode) { + var text = keyNode.getText(); + var occurences = this.usedKeys[text]; + if (!occurences) { + occurences = []; + this.usedKeys[text] = occurences; + } + if (messageNode) { + if (occurences.some(function (pair) { return pair.message ? pair.message.getText() === messageNode.getText() : false; })) { + return; + } + } + occurences.push({ key: keyNode, message: messageNode }); + }; + NoUnexternalizedStringsRuleWalker.prototype.findDescribingParent = function (node) { + var parent; + while ((parent = node.parent)) { + var kind = parent.kind; + if (kind === ts.SyntaxKind.CallExpression) { + var callExpression = parent; + return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; + } + else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { + return { ignoreUsage: true }; + } + else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration + || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration + || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration + || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { + return null; + } + node = parent; + } + }; + return NoUnexternalizedStringsRuleWalker; +}(Lint.RuleWalker)); +NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"'; diff --git a/build/lib/tslint/tsconfig.json b/build/lib/tslint/tsconfig.json deleted file mode 100644 index acb257104ac..00000000000 --- a/build/lib/tslint/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "es2015" - ] - } -} diff --git a/build/lib/util.js b/build/lib/util.js index 8ce093e800f..a6198577fb7 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -1,213 +1,212 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -var es = require("event-stream"); -var debounce = require("debounce"); -var _filter = require("gulp-filter"); -var rename = require("gulp-rename"); -var _ = require("underscore"); -var path = require("path"); -var fs = require("fs"); -var _rimraf = require("rimraf"); -var git = require("./git"); -var VinylFile = require("vinyl"); -var NoCancellationToken = { isCancellationRequested: function () { return false; } }; -function incremental(streamProvider, initial, supportsCancellation) { - var input = es.through(); - var output = es.through(); - var state = 'idle'; - var buffer = Object.create(null); - var token = !supportsCancellation ? null : { isCancellationRequested: function () { return Object.keys(buffer).length > 0; } }; - var run = function (input, isCancellable) { - state = 'running'; - var stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); - input - .pipe(stream) - .pipe(es.through(null, function () { - state = 'idle'; - eventuallyRun(); - })) - .pipe(output); - }; - if (initial) { - run(initial, false); - } - var eventuallyRun = debounce(function () { - var paths = Object.keys(buffer); - if (paths.length === 0) { - return; - } - var data = paths.map(function (path) { return buffer[path]; }); - buffer = Object.create(null); - run(es.readArray(data), true); - }, 500); - input.on('data', function (f) { - buffer[f.path] = f; - if (state === 'idle') { - eventuallyRun(); - } - }); - return es.duplex(input, output); -} -exports.incremental = incremental; -function fixWin32DirectoryPermissions() { - if (!/win32/.test(process.platform)) { - return es.through(); - } - return es.mapSync(function (f) { - if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { - f.stat.mode = 16877; - } - return f; - }); -} -exports.fixWin32DirectoryPermissions = fixWin32DirectoryPermissions; -function setExecutableBit(pattern) { - var setBit = es.mapSync(function (f) { - f.stat.mode = 33261; - return f; - }); - if (!pattern) { - return setBit; - } - var input = es.through(); - var filter = _filter(pattern, { restore: true }); - var output = input - .pipe(filter) - .pipe(setBit) - .pipe(filter.restore); - return es.duplex(input, output); -} -exports.setExecutableBit = setExecutableBit; -function toFileUri(filePath) { - var match = filePath.match(/^([a-z])\:(.*)$/i); - if (match) { - filePath = '/' + match[1].toUpperCase() + ':' + match[2]; - } - return 'file://' + filePath.replace(/\\/g, '/'); -} -exports.toFileUri = toFileUri; -function skipDirectories() { - return es.mapSync(function (f) { - if (!f.isDirectory()) { - return f; - } - }); -} -exports.skipDirectories = skipDirectories; -function cleanNodeModule(name, excludes, includes) { - var toGlob = function (path) { return '**/node_modules/' + name + (path ? '/' + path : ''); }; - var negate = function (str) { return '!' + str; }; - var allFilter = _filter(toGlob('**'), { restore: true }); - var globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); - var input = es.through(); - var nodeModuleInput = input.pipe(allFilter); - var output = nodeModuleInput.pipe(_filter(globs)); - if (includes) { - var includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - output = output.pipe(allFilter.restore); - return es.duplex(input, output); -} -exports.cleanNodeModule = cleanNodeModule; -function loadSourcemaps() { - var input = es.through(); - var output = input - .pipe(es.map(function (f, cb) { - if (f.sourceMap) { - cb(null, f); - return; - } - if (!f.contents) { - cb(new Error('empty file')); - return; - } - var contents = f.contents.toString('utf8'); - var reg = /\/\/# sourceMappingURL=(.*)$/g; - var lastMatch = null, match = null; - while (match = reg.exec(contents)) { - lastMatch = match; - } - if (!lastMatch) { - f.sourceMap = { - version: 3, - names: [], - mappings: '', - sources: [f.relative.replace(/\//g, '/')], - sourcesContent: [contents] - }; - cb(null, f); - return; - } - f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); - fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', function (err, contents) { - if (err) { - return cb(err); - } - f.sourceMap = JSON.parse(contents); - cb(null, f); - }); - })); - return es.duplex(input, output); -} -exports.loadSourcemaps = loadSourcemaps; -function stripSourceMappingURL() { - var input = es.through(); - var output = input - .pipe(es.mapSync(function (f) { - var contents = f.contents.toString('utf8'); - f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); - return f; - })); - return es.duplex(input, output); -} -exports.stripSourceMappingURL = stripSourceMappingURL; -function rimraf(dir) { - var retries = 0; - var retry = function (cb) { - _rimraf(dir, { maxBusyTries: 1 }, function (err) { - if (!err) { - return cb(); - } - ; - if (err.code === 'ENOTEMPTY' && ++retries < 5) { - return setTimeout(function () { return retry(cb); }, 10); - } - return cb(err); - }); - }; - return function (cb) { return retry(cb); }; -} -exports.rimraf = rimraf; -function getVersion(root) { - var version = process.env['BUILD_SOURCEVERSION']; - if (!version || !/^[0-9a-f]{40}$/i.test(version)) { - version = git.getVersion(root); - } - return version; -} -exports.getVersion = getVersion; -function rebase(count) { - return rename(function (f) { - var parts = f.dirname.split(/[\/\\]/); - f.dirname = parts.slice(count).join(path.sep); - }); -} -exports.rebase = rebase; -function filter(fn) { - var result = es.through(function (data) { - if (fn(data)) { - this.emit('data', data); - } - else { - result.restore.push(data); - } - }); - result.restore = es.through(); - return result; -} -exports.filter = filter; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +var es = require("event-stream"); +var debounce = require("debounce"); +var _filter = require("gulp-filter"); +var rename = require("gulp-rename"); +var _ = require("underscore"); +var path = require("path"); +var fs = require("fs"); +var _rimraf = require("rimraf"); +var git = require("./git"); +var VinylFile = require("vinyl"); +var NoCancellationToken = { isCancellationRequested: function () { return false; } }; +function incremental(streamProvider, initial, supportsCancellation) { + var input = es.through(); + var output = es.through(); + var state = 'idle'; + var buffer = Object.create(null); + var token = !supportsCancellation ? null : { isCancellationRequested: function () { return Object.keys(buffer).length > 0; } }; + var run = function (input, isCancellable) { + state = 'running'; + var stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); + input + .pipe(stream) + .pipe(es.through(null, function () { + state = 'idle'; + eventuallyRun(); + })) + .pipe(output); + }; + if (initial) { + run(initial, false); + } + var eventuallyRun = debounce(function () { + var paths = Object.keys(buffer); + if (paths.length === 0) { + return; + } + var data = paths.map(function (path) { return buffer[path]; }); + buffer = Object.create(null); + run(es.readArray(data), true); + }, 500); + input.on('data', function (f) { + buffer[f.path] = f; + if (state === 'idle') { + eventuallyRun(); + } + }); + return es.duplex(input, output); +} +exports.incremental = incremental; +function fixWin32DirectoryPermissions() { + if (!/win32/.test(process.platform)) { + return es.through(); + } + return es.mapSync(function (f) { + if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { + f.stat.mode = 16877; + } + return f; + }); +} +exports.fixWin32DirectoryPermissions = fixWin32DirectoryPermissions; +function setExecutableBit(pattern) { + var setBit = es.mapSync(function (f) { + f.stat.mode = 33261; + return f; + }); + if (!pattern) { + return setBit; + } + var input = es.through(); + var filter = _filter(pattern, { restore: true }); + var output = input + .pipe(filter) + .pipe(setBit) + .pipe(filter.restore); + return es.duplex(input, output); +} +exports.setExecutableBit = setExecutableBit; +function toFileUri(filePath) { + var match = filePath.match(/^([a-z])\:(.*)$/i); + if (match) { + filePath = '/' + match[1].toUpperCase() + ':' + match[2]; + } + return 'file://' + filePath.replace(/\\/g, '/'); +} +exports.toFileUri = toFileUri; +function skipDirectories() { + return es.mapSync(function (f) { + if (!f.isDirectory()) { + return f; + } + }); +} +exports.skipDirectories = skipDirectories; +function cleanNodeModule(name, excludes, includes) { + var toGlob = function (path) { return '**/node_modules/' + name + (path ? '/' + path : ''); }; + var negate = function (str) { return '!' + str; }; + var allFilter = _filter(toGlob('**'), { restore: true }); + var globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); + var input = es.through(); + var nodeModuleInput = input.pipe(allFilter); + var output = nodeModuleInput.pipe(_filter(globs)); + if (includes) { + var includeGlobs = includes.map(toGlob); + output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); + } + output = output.pipe(allFilter.restore); + return es.duplex(input, output); +} +exports.cleanNodeModule = cleanNodeModule; +function loadSourcemaps() { + var input = es.through(); + var output = input + .pipe(es.map(function (f, cb) { + if (f.sourceMap) { + cb(null, f); + return; + } + if (!f.contents) { + cb(new Error('empty file')); + return; + } + var contents = f.contents.toString('utf8'); + var reg = /\/\/# sourceMappingURL=(.*)$/g; + var lastMatch = null, match = null; + while (match = reg.exec(contents)) { + lastMatch = match; + } + if (!lastMatch) { + f.sourceMap = { + version: 3, + names: [], + mappings: '', + sources: [f.relative.replace(/\//g, '/')], + sourcesContent: [contents] + }; + cb(null, f); + return; + } + f.contents = new Buffer(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); + fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', function (err, contents) { + if (err) { + return cb(err); + } + f.sourceMap = JSON.parse(contents); + cb(null, f); + }); + })); + return es.duplex(input, output); +} +exports.loadSourcemaps = loadSourcemaps; +function stripSourceMappingURL() { + var input = es.through(); + var output = input + .pipe(es.mapSync(function (f) { + var contents = f.contents.toString('utf8'); + f.contents = new Buffer(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); + return f; + })); + return es.duplex(input, output); +} +exports.stripSourceMappingURL = stripSourceMappingURL; +function rimraf(dir) { + var retries = 0; + var retry = function (cb) { + _rimraf(dir, { maxBusyTries: 1 }, function (err) { + if (!err) { + return cb(); + } + ; + if (err.code === 'ENOTEMPTY' && ++retries < 5) { + return setTimeout(function () { return retry(cb); }, 10); + } + return cb(err); + }); + }; + return function (cb) { return retry(cb); }; +} +exports.rimraf = rimraf; +function getVersion(root) { + var version = process.env['BUILD_SOURCEVERSION']; + if (!version || !/^[0-9a-f]{40}$/i.test(version)) { + version = git.getVersion(root); + } + return version; +} +exports.getVersion = getVersion; +function rebase(count) { + return rename(function (f) { + var parts = f.dirname.split(/[\/\\]/); + f.dirname = parts.slice(count).join(path.sep); + }); +} +exports.rebase = rebase; +function filter(fn) { + var result = es.through(function (data) { + if (fn(data)) { + this.emit('data', data); + } + else { + result.restore.push(data); + } + }); + result.restore = es.through(); + return result; +} +exports.filter = filter; diff --git a/build/monaco/api.js b/build/monaco/api.js index c6498ad3daa..c2863eadaec 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -1,346 +1,343 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var fs = require("fs"); -var ts = require("typescript"); -var path = require("path"); -var util = require('gulp-util'); -function log(message) { - var rest = []; - for (var _i = 1; _i < arguments.length; _i++) { - rest[_i - 1] = arguments[_i]; - } - util.log.apply(util, [util.colors.cyan('[monaco.d.ts]'), message].concat(rest)); -} -var SRC = path.join(__dirname, '../../src'); -var OUT_ROOT = path.join(__dirname, '../../'); -var RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); -var DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -var CURRENT_PROCESSING_RULE = ''; -function logErr(message) { - var rest = []; - for (var _i = 1; _i < arguments.length; _i++) { - rest[_i - 1] = arguments[_i]; - } - util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); - util.log.apply(util, [util.colors.red('[monaco.d.ts]'), message].concat(rest)); -} -function moduleIdToPath(out, moduleId) { - if (/\.d\.ts/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; -} -var SOURCE_FILE_MAP = {}; -function getSourceFile(out, inputFiles, moduleId) { - if (!SOURCE_FILE_MAP[moduleId]) { - var filePath = path.normalize(moduleIdToPath(out, moduleId)); - if (!inputFiles.hasOwnProperty(filePath)) { - logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); - return null; - } - var fileContents = inputFiles[filePath]; - var sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); - SOURCE_FILE_MAP[moduleId] = sourceFile; - } - return SOURCE_FILE_MAP[moduleId]; -} -function isDeclaration(a) { - return (a.kind === ts.SyntaxKind.InterfaceDeclaration - || a.kind === ts.SyntaxKind.EnumDeclaration - || a.kind === ts.SyntaxKind.ClassDeclaration - || a.kind === ts.SyntaxKind.TypeAliasDeclaration - || a.kind === ts.SyntaxKind.FunctionDeclaration - || a.kind === ts.SyntaxKind.ModuleDeclaration); -} -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: - case ts.SyntaxKind.VariableStatement: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.ModuleDeclaration: - stop = visitor(node); - } - // if (node.kind !== ts.SyntaxKind.SourceFile) { - // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { - // console.log('FOUND TEXT IN NODE: ' + ts.SyntaxKind[node.kind]); - // console.log(getNodeText(sourceFile, node)); - // } - // } - if (stop) { - return; - } - ts.forEachChild(node, visit); - }; - visit(sourceFile); -} -function getAllTopLevelDeclarations(sourceFile) { - var all = []; - visitTopLevelDeclarations(sourceFile, function (node) { - if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { - var interfaceDeclaration = node; - var triviaStart = interfaceDeclaration.pos; - var triviaEnd = interfaceDeclaration.name.pos; - var triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); - // // let nodeText = getNodeText(sourceFile, node); - // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { - // console.log('TRIVIA: ', triviaText); - // } - if (triviaText.indexOf('@internal') === -1) { - all.push(node); - } - } - else { - var nodeText = getNodeText(sourceFile, node); - if (nodeText.indexOf('@internal') === -1) { - all.push(node); - } - } - return false /*continue*/; - }); - return all; -} -function getTopLevelDeclaration(sourceFile, typeName) { - var result = null; - visitTopLevelDeclarations(sourceFile, function (node) { - if (isDeclaration(node)) { - if (node.name.text === typeName) { - result = node; - return true /*stop*/; - } - return false /*continue*/; - } - // node is ts.VariableStatement - if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) { - 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); - // if (result.indexOf('MonacoWorker') >= 0) { - // console.log('here!'); - // // console.log(ts.SyntaxKind[declaration.kind]); - // } - if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { - var interfaceDeclaration = declaration; - var members = interfaceDeclaration.members; - members.forEach(function (member) { - try { - var memberText = getNodeText(sourceFile, member); - if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { - // console.log('BEFORE: ', result); - result = result.replace(memberText, ''); - // console.log('AFTER: ', result); - } - } - catch (err) { - // life.. - } - }); - } - result = result.replace(/export default/g, 'export'); - result = result.replace(/export declare/g, 'export'); - return result; -} -function format(text) { - var options = getDefaultOptions(); - // Parse the source text - var sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); - // Get the formatting edits on the input sources - var edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(options), options); - // Apply the edits on the input code - return applyEdits(text, edits); - function getRuleProvider(options) { - // Share this between multiple formatters using the same options. - // This represents the bulk of the space the formatter uses. - var ruleProvider = new ts.formatting.RulesProvider(); - ruleProvider.ensureUpToDate(options); - return ruleProvider; - } - function applyEdits(text, edits) { - // Apply edits in reverse on the existing text - var result = text; - for (var i = edits.length - 1; i >= 0; i--) { - var change = edits[i]; - var head = result.slice(0, change.span.start); - var tail = result.slice(change.span.start + change.span.length); - result = head + change.newText + tail; - } - return result; - } - function getDefaultOptions() { - return { - indentSize: 4, - tabSize: 4, - newLineCharacter: '\r\n', - convertTabsToSpaces: true, - indentStyle: ts.IndentStyle.Block, - insertSpaceAfterCommaDelimiter: true, - insertSpaceAfterSemicolonInForStatements: true, - insertSpaceBeforeAndAfterBinaryOperators: true, - insertSpaceAfterKeywordsInControlFlowStatements: true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, - placeOpenBraceOnNewLineForFunctions: false, - placeOpenBraceOnNewLineForControlBlocks: false, - }; - } -} -function createReplacer(data) { - data = data || ''; - var rawDirectives = data.split(';'); - var directives = []; - rawDirectives.forEach(function (rawDirective) { - if (rawDirective.length === 0) { - return; - } - var pieces = rawDirective.split('=>'); - var findStr = pieces[0]; - var replaceStr = pieces[1]; - findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); - findStr = '\\b' + findStr + '\\b'; - directives.push([new RegExp(findStr, 'g'), replaceStr]); - }); - return function (str) { - for (var i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; -} -function generateDeclarationFile(out, inputFiles, recipe) { - var lines = recipe.split(/\r\n|\n|\r/); - var result = []; - lines.forEach(function (line) { - var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - CURRENT_PROCESSING_RULE = line; - var moduleId = m1[1]; - var sourceFile_1 = getSourceFile(out, inputFiles, moduleId); - if (!sourceFile_1) { - return; - } - var replacer_1 = createReplacer(m1[2]); - var typeNames = m1[3].split(/,/); - typeNames.forEach(function (typeName) { - typeName = typeName.trim(); - if (typeName.length === 0) { - return; - } - var declaration = getTopLevelDeclaration(sourceFile_1, typeName); - if (!declaration) { - logErr('Cannot find type ' + typeName); - return; - } - result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration))); - }); - return; - } - var m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - CURRENT_PROCESSING_RULE = line; - var moduleId = m2[1]; - var sourceFile_2 = getSourceFile(out, inputFiles, moduleId); - if (!sourceFile_2) { - return; - } - var replacer_2 = createReplacer(m2[2]); - var typeNames = m2[3].split(/,/); - var typesToExcludeMap_1 = {}; - var typesToExcludeArr_1 = []; - typeNames.forEach(function (typeName) { - typeName = typeName.trim(); - if (typeName.length === 0) { - return; - } - typesToExcludeMap_1[typeName] = true; - typesToExcludeArr_1.push(typeName); - }); - getAllTopLevelDeclarations(sourceFile_2).forEach(function (declaration) { - if (isDeclaration(declaration)) { - if (typesToExcludeMap_1[declaration.name.text]) { - return; - } - } - else { - // node is ts.VariableStatement - var nodeText = getNodeText(sourceFile_2, declaration); - for (var i = 0; i < typesToExcludeArr_1.length; i++) { - if (nodeText.indexOf(typesToExcludeArr_1[i]) >= 0) { - return; - } - } - } - result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration))); - }); - return; - } - result.push(line); - }); - var resultTxt = result.join('\n'); - resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); - resultTxt = resultTxt.replace(/\bEvent= 0) { + // console.log('FOUND TEXT IN NODE: ' + ts.SyntaxKind[node.kind]); + // console.log(getNodeText(sourceFile, node)); + // } + // } + if (stop) { + return; + } + ts.forEachChild(node, visit); + }; + visit(sourceFile); +} +function getAllTopLevelDeclarations(sourceFile) { + var all = []; + visitTopLevelDeclarations(sourceFile, function (node) { + if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { + var interfaceDeclaration = node; + var triviaStart = interfaceDeclaration.pos; + var triviaEnd = interfaceDeclaration.name.pos; + var triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); + // // let nodeText = getNodeText(sourceFile, node); + // if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) { + // console.log('TRIVIA: ', triviaText); + // } + if (triviaText.indexOf('@internal') === -1) { + all.push(node); + } + } + else { + var nodeText = getNodeText(sourceFile, node); + if (nodeText.indexOf('@internal') === -1) { + all.push(node); + } + } + return false /*continue*/; + }); + return all; +} +function getTopLevelDeclaration(sourceFile, typeName) { + var result = null; + visitTopLevelDeclarations(sourceFile, function (node) { + if (isDeclaration(node)) { + if (node.name.text === typeName) { + result = node; + return true /*stop*/; + } + return false /*continue*/; + } + // node is ts.VariableStatement + if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) { + 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); + // if (result.indexOf('MonacoWorker') >= 0) { + // console.log('here!'); + // // console.log(ts.SyntaxKind[declaration.kind]); + // } + if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { + var interfaceDeclaration = declaration; + var members = interfaceDeclaration.members; + members.forEach(function (member) { + try { + var memberText = getNodeText(sourceFile, member); + if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { + // console.log('BEFORE: ', result); + result = result.replace(memberText, ''); + } + } + catch (err) { + } + }); + } + result = result.replace(/export default/g, 'export'); + result = result.replace(/export declare/g, 'export'); + return result; +} +function format(text) { + var options = getDefaultOptions(); + // Parse the source text + var sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); + // Get the formatting edits on the input sources + var edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(options), options); + // Apply the edits on the input code + return applyEdits(text, edits); + function getRuleProvider(options) { + // Share this between multiple formatters using the same options. + // This represents the bulk of the space the formatter uses. + var ruleProvider = new ts.formatting.RulesProvider(); + ruleProvider.ensureUpToDate(options); + return ruleProvider; + } + function applyEdits(text, edits) { + // Apply edits in reverse on the existing text + var result = text; + for (var i = edits.length - 1; i >= 0; i--) { + var change = edits[i]; + var head = result.slice(0, change.span.start); + var tail = result.slice(change.span.start + change.span.length); + result = head + change.newText + tail; + } + return result; + } + function getDefaultOptions() { + return { + indentSize: 4, + tabSize: 4, + newLineCharacter: '\r\n', + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Block, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + }; + } +} +function createReplacer(data) { + data = data || ''; + var rawDirectives = data.split(';'); + var directives = []; + rawDirectives.forEach(function (rawDirective) { + if (rawDirective.length === 0) { + return; + } + var pieces = rawDirective.split('=>'); + var findStr = pieces[0]; + var replaceStr = pieces[1]; + findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); + findStr = '\\b' + findStr + '\\b'; + directives.push([new RegExp(findStr, 'g'), replaceStr]); + }); + return function (str) { + for (var i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} +function generateDeclarationFile(out, inputFiles, recipe) { + var lines = recipe.split(/\r\n|\n|\r/); + var result = []; + lines.forEach(function (line) { + var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m1) { + CURRENT_PROCESSING_RULE = line; + var moduleId = m1[1]; + var sourceFile_1 = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile_1) { + return; + } + var replacer_1 = createReplacer(m1[2]); + var typeNames = m1[3].split(/,/); + typeNames.forEach(function (typeName) { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + var declaration = getTopLevelDeclaration(sourceFile_1, typeName); + if (!declaration) { + logErr('Cannot find type ' + typeName); + return; + } + result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration))); + }); + return; + } + var m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + if (m2) { + CURRENT_PROCESSING_RULE = line; + var moduleId = m2[1]; + var sourceFile_2 = getSourceFile(out, inputFiles, moduleId); + if (!sourceFile_2) { + return; + } + var replacer_2 = createReplacer(m2[2]); + var typeNames = m2[3].split(/,/); + var typesToExcludeMap_1 = {}; + var typesToExcludeArr_1 = []; + typeNames.forEach(function (typeName) { + typeName = typeName.trim(); + if (typeName.length === 0) { + return; + } + typesToExcludeMap_1[typeName] = true; + typesToExcludeArr_1.push(typeName); + }); + getAllTopLevelDeclarations(sourceFile_2).forEach(function (declaration) { + if (isDeclaration(declaration)) { + if (typesToExcludeMap_1[declaration.name.text]) { + return; + } + } + else { + // node is ts.VariableStatement + var nodeText = getNodeText(sourceFile_2, declaration); + for (var i = 0; i < typesToExcludeArr_1.length; i++) { + if (nodeText.indexOf(typesToExcludeArr_1[i]) >= 0) { + return; + } + } + } + result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration))); + }); + return; + } + result.push(line); + }); + var resultTxt = result.join('\n'); + resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); + resultTxt = resultTxt.replace(/\bEventlanguages.;editorCommon.=>): -#include(vs/editor/common/services/standaloneColorService): BuiltinTheme, ITheme -#include(vs/editor/common/modes/supports/tokenization): IThemeRule +#include(vs/editor/common/services/standaloneThemeService): BuiltinTheme, IStandaloneThemeData, IColors +#include(vs/editor/common/modes/supports/tokenization): ITokenThemeRule #include(vs/editor/common/services/webWorker): MonacoWebWorker, IWebWorkerOptions #include(vs/editor/browser/standalone/standaloneCodeEditor): IEditorConstructionOptions, IDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index ad4a995d531..3cdf9461078 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -11,6 +11,7 @@ import { Model, Resource, Status, CommitOptions } from './model'; import * as staging from './staging'; import * as path from 'path'; import * as os from 'os'; +import { uniqueFilter } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; @@ -272,14 +273,14 @@ export class CommandCenter { } @command('git.stage') - async stage(uri?: Uri): Promise { - const resource = this.resolveSCMResource(uri); + async stage(...uris: Uri[]): Promise { + const resources = this.toSCMResources(uris); - if (!resource) { + if (!resources.length) { return; } - return await this.model.add(resource); + return await this.model.add(...resources); } @command('git.stageAll') @@ -369,14 +370,14 @@ export class CommandCenter { } @command('git.unstage') - async unstage(uri?: Uri): Promise { - const resource = this.resolveSCMResource(uri); + async unstage(...uris: Uri[]): Promise { + const resources = this.toSCMResources(uris); - if (!resource) { + if (!resources.length) { return; } - return await this.model.revertFiles(resource); + return await this.model.revertFiles(...resources); } @command('git.unstageAll') @@ -427,15 +428,17 @@ export class CommandCenter { } @command('git.clean') - async clean(uri?: Uri): Promise { - const resource = this.resolveSCMResource(uri); + async clean(...uris: Uri[]): Promise { + const resources = this.toSCMResources(uris); - if (!resource) { + if (!resources.length) { return; } - const basename = path.basename(resource.uri.fsPath); - const message = localize('confirm discard', "Are you sure you want to discard changes in {0}?", basename); + const message = resources.length === 1 + ? localize('confirm discard', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].uri.fsPath)) + : localize('confirm discard multiple', "Are you sure you want to discard changes in {0} files?", resources.length); + const yes = localize('discard', "Discard Changes"); const pick = await window.showWarningMessage(message, { modal: true }, yes); @@ -443,7 +446,7 @@ export class CommandCenter { return; } - await this.model.clean(resource); + await this.model.clean(...resources); } @command('git.cleanAll') @@ -773,7 +776,7 @@ export class CommandCenter { uri = uri || window.activeTextEditor && window.activeTextEditor.document.uri; if (!uri) { - return; + return undefined; } if (uri.scheme === 'scm' && uri.authority === 'git') { @@ -793,6 +796,12 @@ export class CommandCenter { } } + private toSCMResources(uris: Uri[]): Resource[] { + return uris.filter(uniqueFilter(uri => uri.toString())) + .map(uri => this.resolveSCMResource(uri)) + .filter(r => !!r) as Resource[]; + } + dispose(): void { this.disposables.forEach(d => d.dispose()); } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 95d89cd16de..29888e54890 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -147,4 +147,19 @@ export async function mkdirp(path: string, mode?: number): Promise { } return true; +} + +export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { + const seen: { [key: string]: boolean; } = Object.create(null); + + return element => { + const key = keyFn(element); + + if (seen[key]) { + return false; + } + + seen[key] = true; + return true; + }; } \ No newline at end of file diff --git a/extensions/javascript/src/features/jsonContributions.ts b/extensions/javascript/src/features/jsonContributions.ts index 52df5e56ed7..6aca3ebd416 100644 --- a/extensions/javascript/src/features/jsonContributions.ts +++ b/extensions/javascript/src/features/jsonContributions.ts @@ -36,7 +36,7 @@ export function addJSONProviders(xhr: XHRRequest): Disposable { let subscriptions: Disposable[] = []; contributions.forEach(contribution => { let selector = contribution.getDocumentSelector(); - subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution), '.', '$')); + subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution), '"', ':')); subscriptions.push(languages.registerHoverProvider(selector, new JSONHoverProvider(contribution))); }); return Disposable.from(...subscriptions); diff --git a/extensions/json/package.json b/extensions/json/package.json index dbb2ac7eaba..5b5fa7771cd 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -109,6 +109,11 @@ "description": "Traces the communication between VS Code and the JSON language server." } } + }, + "configurationDefaults": { + "[json]": { + "editor.quickSuggestions": { "strings": true } + } } }, "dependencies": { diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index 2961bfa3745..1af59591246 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -40,7 +40,7 @@ interface PreviewSecurityPickItem extends vscode.QuickPickItem { id: PreviewSecuritySelection; } -class ExtensionContentSecurityProlicyArbiter implements ContentSecurityPolicyArbiter { +class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter { private readonly key = 'trusted_preview_workspace:'; constructor( @@ -58,9 +58,16 @@ class ExtensionContentSecurityProlicyArbiter implements ContentSecurityPolicyArb public removeTrustedWorkspace(rootPath: string): Thenable { return this.globalState.update(this.key + rootPath, false); } - } +const resolveExtensionResources = (extension: vscode.Extension, stylePath: string): vscode.Uri => { + const resource = vscode.Uri.parse(stylePath); + if (resource.scheme) { + return resource; + } + return vscode.Uri.file(path.join(extension.extensionPath, stylePath)); +}; + var telemetryReporter: TelemetryReporter | null; export function activate(context: vscode.ExtensionContext) { @@ -70,12 +77,43 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(telemetryReporter); } - const cspArbiter = new ExtensionContentSecurityProlicyArbiter(context.globalState); + const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState); const engine = new MarkdownEngine(); const contentProvider = new MDDocumentContentProvider(engine, context, cspArbiter); const contentProviderRegistration = vscode.workspace.registerTextDocumentContentProvider('markdown', contentProvider); + if (vscode.workspace.getConfiguration('markdown').get('enableExperimentalExtensionApi', false)) { + for (const extension of vscode.extensions.all) { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + if (!contributes) { + continue; + } + + let styles = contributes['markdown.preview'] && contributes['markdown.preview'].styles; + if (styles) { + if (!Array.isArray(styles)) { + styles = [styles]; + } + for (const style of styles) { + try { + contentProvider.addStyle(resolveExtensionResources(extension, style)); + } catch (e) { + // noop + } + } + } + + if (contributes['markdownit.plugins']) { + extension.activate().then(() => { + if (extension.exports && extension.exports.extendMarkdownIt) { + engine.addPlugin((md: any) => extension.exports.extendMarkdownIt(md)); + } + }); + } + } + } + const symbolsProvider = new MDDocumentSymbolProvider(engine); const symbolsProviderRegistration = vscode.languages.registerDocumentSymbolProvider({ language: 'markdown' }, symbolsProvider); context.subscriptions.push(contentProviderRegistration, symbolsProviderRegistration); @@ -231,18 +269,6 @@ export function activate(context: vscode.ExtensionContext) { }); } })); - - if (vscode.workspace.getConfiguration('markdown').get('enableExperimentalExtensionApi', false)) { - vscode.commands.executeCommand('_markdown.onActivateExtensions') - .then(() => void 0, () => void 0); - - return { - addPlugin(factory: (md: any) => any) { - engine.addPlugin(factory); - } - }; - } - return undefined; } diff --git a/extensions/markdown/src/markdownEngine.ts b/extensions/markdown/src/markdownEngine.ts index f8dfdb1a6b5..6c6c9e3dffb 100644 --- a/extensions/markdown/src/markdownEngine.ts +++ b/extensions/markdown/src/markdownEngine.ts @@ -33,8 +33,6 @@ export class MarkdownEngine { private plugins: Array<(md: any) => any> = []; - constructor() { } - public addPlugin(factory: (md: any) => any): void { if (this.md) { this.usePlugin(factory); diff --git a/extensions/markdown/src/previewContentProvider.ts b/extensions/markdown/src/previewContentProvider.ts index 9de21f27063..078335d0d6b 100644 --- a/extensions/markdown/src/previewContentProvider.ts +++ b/extensions/markdown/src/previewContentProvider.ts @@ -28,6 +28,7 @@ export function getMarkdownUri(uri: vscode.Uri) { export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider { private _onDidChange = new vscode.EventEmitter(); private _waiting: boolean = false; + private extraStyles: Array = []; constructor( private engine: MarkdownEngine, @@ -35,6 +36,10 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv private cspArbiter: ContentSecurityPolicyArbiter ) { } + public addStyle(resource: vscode.Uri): void { + this.extraStyles.push(resource); + } + private getMediaPath(mediaFile: string): string { return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))).toString(); } @@ -97,7 +102,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv const baseStyles = [ this.getMediaPath('markdown.css'), this.getMediaPath('tomorrow.css') - ]; + ].concat(this.extraStyles.map(resource => resource.toString())); return `${baseStyles.map(href => ``).join('\n')} ${this.getSettingsOverrideStyles(nonce)} @@ -105,8 +110,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv } private getScripts(nonce: string): string { - const scripts = [this.getMediaPath('main.js')]; - return scripts + return [this.getMediaPath('main.js')] .map(source => ``) .join('\n'); } diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json index 3508657f5ce..39c4bb4712f 100644 --- a/extensions/npm-shrinkwrap.json +++ b/extensions/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "0.0.1", "dependencies": { "typescript": { - "version": "2.2.1", - "from": "typescript@typescript@2.2.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.1.tgz" + "version": "2.2.2-insiders.20170317", + "from": "typescript@typescript@2.2.2-insiders.20170317", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.2-insiders.20170317.tgz" } } } diff --git a/extensions/package.json b/extensions/package.json index 951b80e864b..295013fa3fa 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "2.2.1" + "typescript": "2.2.2-insiders.20170317" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index b673068313d..69712acbbba 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -256,6 +256,29 @@ "editorInvisibles": "#002040", "editorLineHighlight": "#082050", "editorSelection": "#770811", - "editorGuide": "#002952" + "editorGuide": "#002952", + "editorHoverBackground": "#000c38", + "editorHoverBorder": "#004c18", + "editorPeekResultsBackground": "#060621", + "editorPeekResultsMatchForeground": "#7777cc", + "editorPeekResultsSelectedBackground": "#070877", + "editorPeekResultsSelectedForeground": "#77EEFF", + "editorPeekEditorBackground": "#001F33", + "editorPeekTitleBackground": "#060621", + "editorPeekBorders": "#7777cc", + + // Workbench Colors + "tabsContainerBackground": "#1c1c2a", + "inactiveTabBackground": "#10192c", + "tabBorder": "#2b2b4a", + "editorGroupBorder": "#2b2b4a", + "editorGroupBackground": "#1c1c2a", + "editorDragAndDropBackground": "#25375daa", + "panelBorderTopColor": "#2b2b4a", + "statusBarBackground": "#10192c", + "activityBarBackground": "#051336", + "sideBarBackground": "#060621", + "titleBarActiveBackground": "#10192c", + "titleBarInactiveBackground": "#10192caa" } } \ No newline at end of file diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index c5721ef1c0c..65e89389642 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -34,6 +34,7 @@ "onCommand:typescript.selectTypeScriptVersion", "onCommand:javascript.goToProjectConfig", "onCommand:typescript.goToProjectConfig", + "onCommand:typescript.openTsServerLog", "workspaceContains:jsconfig.json", "workspaceContains:tsconfig.json" ], @@ -114,6 +115,17 @@ "default": false, "description": "%typescript.implementationsCodeLens.enabled%" }, + "typescript.tsserver.log": { + "type": "string", + "enum": [ + "off", + "terse", + "normal", + "verbose" + ], + "default": "off", + "description": "%typescript.tsserver.log%" + }, "typescript.tsserver.trace": { "type": "string", "enum": [ @@ -296,6 +308,11 @@ "command": "javascript.goToProjectConfig", "title": "%javascript.goToProjectConfig.title%", "category": "JavaScript" + }, + { + "command": "typescript.openTsServerLog", + "title": "%typescript.openTsServerLog.title%", + "category": "TypeScript" } ], "menus": { diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index 9746cfc13a4..a96e74dc762 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -7,6 +7,7 @@ "typescript.tsdk_version.desc": "Specifies the version of the tsserver. Only necessary if the tsserver is not installed using npm.", "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Requires TypeScript >= 2.0.6 and a restart after changing it.", "typescript.check.tscVersion": "Check if a global install TypeScript compiler (e.g. tsc) differs from the used TypeScript language service.", + "typescript.tsserver.log": "Enables logging of the TS server to a file.", "typescript.tsserver.trace": "Enables tracing of messages sent to the TS server.", "typescript.tsserver.experimentalAutoBuild": "Enables experimental auto build. Requires 1.9 dev or 2.x tsserver version and a restart of VS Code after changing it.", "typescript.validate.enable": "Enable/disable TypeScript validation.", @@ -29,5 +30,6 @@ "javascript.goToProjectConfig.title": "Go to Project Configuration", "typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens.", "typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens.", + "typescript.openTsServerLog.title": "Open TS Server log file", "typescript.selectTypeScriptVersion.title": "Select TypeScript Version" } diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 4dee65fa959..06969028436 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -104,6 +104,10 @@ export function activate(context: ExtensionContext): void { client.onVersionStatusClicked(); })); + context.subscriptions.push(commands.registerCommand('typescript.openTsServerLog', () => { + client.openTsServerLogFile(); + })); + context.subscriptions.push( languages.registerCompletionItemProvider(selector, new JsDocCompletionHelper(client), '*')); diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index fc191c414c1..289c227daf3 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -57,6 +57,10 @@ export class API { public has220Features(): boolean { return semver.gte(this._version, '2.2.0'); } + + public has222Features(): boolean { + return semver.gte(this._version, '2.2.2'); + } } export interface ITypescriptServiceClient { diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index 415511f01ed..79bc33017d8 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -8,6 +8,7 @@ import * as cp from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; +import * as os from 'os'; import * as electron from './utils/electron'; import { Reader } from './utils/wireProtocol'; @@ -47,7 +48,9 @@ interface IPackageInfo { } enum Trace { - Off, Messages, Verbose + Off, + Messages, + Verbose } namespace Trace { @@ -66,6 +69,43 @@ namespace Trace { } } +enum TsServerLogLevel { + Off, + Normal, + Terse, + Verbose, +} + +namespace TsServerLogLevel { + export function fromString(value: string): TsServerLogLevel { + switch (value && value.toLowerCase()) { + case 'normal': + return TsServerLogLevel.Normal; + case 'terse': + return TsServerLogLevel.Terse; + case 'verbose': + return TsServerLogLevel.Verbose; + case 'off': + default: + return TsServerLogLevel.Off; + } + } + + export function toString(value: TsServerLogLevel): string { + switch (value) { + case TsServerLogLevel.Normal: + return 'normal'; + case TsServerLogLevel.Terse: + return 'terse'; + case TsServerLogLevel.Verbose: + return 'verbose'; + case TsServerLogLevel.Off: + default: + return 'off'; + } + } +} + enum MessageAction { useLocal, useBundled, @@ -84,7 +124,6 @@ interface MyMessageItem extends MessageItem { export default class TypeScriptServiceClient implements ITypescriptServiceClient { - private static useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; private static tsdkMigratedStorageKey = 'typescript.tsdkMigrated'; @@ -104,6 +143,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient private _experimentalAutoBuild: boolean; private trace: Trace; private _output: OutputChannel; + private tsServerLogFile: string | null = null; + private tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; private servicePromise: Promise | null; private lastError: Error | null; private reader: Reader; @@ -112,6 +153,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient private firstStart: number; private lastStart: number; private numberRestarts: number; + private cancellationPipeName: string | null = null; private requestQueue: RequestItem[]; private pendingResponses: number; @@ -155,6 +197,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient this._apiVersion = new API('1.0.0'); this._checkGlobalTSCVersion = true; this.trace = this.readTrace(); + this.tsServerLogLevel = this.readTsServerLogLevel(); disposables.push(workspace.onDidChangeConfiguration(() => { this.trace = this.readTrace(); let oldglobalTsdk = this.globalTsdk; @@ -221,6 +264,11 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient return result; } + private readTsServerLogLevel(): TsServerLogLevel { + const setting = workspace.getConfiguration().get('typescript.tsserver.log', 'off'); + return TsServerLogLevel.fromString(setting); + } + public get experimentalAutoBuild(): boolean { return this._experimentalAutoBuild; } @@ -453,6 +501,27 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient if (this.apiVersion.has208Features()) { args.push('--enableTelemetry'); } + if (this.apiVersion.has220Features()) { + this.cancellationPipeName = electron.getPipeName(`tscancellation-${electron.makeRandomHexString(20)}`); + args.push('--cancellationPipeName', this.cancellationPipeName + '*'); + } + + if (this.apiVersion.has222Features()) { + if (this.tsServerLogLevel !== TsServerLogLevel.Off) { + try { + const logDir = fs.mkdtempSync(path.join(os.tmpdir(), `vscode-tsserver-log-`)); + this.tsServerLogFile = path.join(logDir, `tsserver.log`); + } catch (e) { + this.error('Could not create TSServer log directory'); + } + + if (this.tsServerLogFile) { + args.push('--logVerbosity', TsServerLogLevel.toString(this.tsServerLogLevel)); + args.push('--logFile', this.tsServerLogFile); + } + } + } + electron.fork(modulePath, args, options, (err: any, childProcess: cp.ChildProcess) => { if (err) { this.lastError = err; @@ -578,6 +647,47 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient }); } + public openTsServerLogFile(): Thenable { + if (!this.apiVersion.has222Features()) { + return window.showErrorMessage( + localize( + 'typescript.openTsServerLog.notSupported', + 'TS Server logging requires TS 2.2.2+')) + .then(() => false); + } + + if (this.tsServerLogLevel === TsServerLogLevel.Off) { + return window.showErrorMessage( + localize( + 'typescript.openTsServerLog.loggingNotEnabled', + 'TS Server logging is off. Please set `typescript.tsserver.log` and reload VS Code to enable logging'), + { + title: localize( + 'typescript.openTsServerLog.enableAndReloadOption', + 'Enable logging and reload VS Code'), + }) + .then(selection => { + if (selection) { + return workspace.getConfiguration().update('typescript.tsserver.log', 'verbose', true).then(() => { + commands.executeCommand('workbench.action.reloadWindow'); + return false; + }); + } + return false; + }); + } + + if (!this.tsServerLogFile) { + return window.showWarningMessage(localize( + 'typescript.openTsServerLog.noLogFile', + 'TS Server has not started logging.')).then(() => false); + } + + return workspace.openTextDocument(this.tsServerLogFile) + .then(doc => window.showTextDocument(doc, window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined)) + .then(editor => !!editor); + } + private serviceStarted(resendModels: boolean): void { let configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode' @@ -588,7 +698,6 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient } catch (error) { } // configureOptions.autoDiagnostics = true; - // configureOptions.metaDataDirectory = this.storagePath; } this.execute('configure', configureOptions); if (this.apiVersion.has206Features()) { @@ -795,6 +904,19 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient return true; } } + + if (this.apiVersion.has220Features() && this.cancellationPipeName) { + if (this.trace !== Trace.Off) { + this.logTrace(`TypeScript Service: trying to cancel ongoing request with sequence number ${seq}`); + } + try { + fs.writeFileSync(this.cancellationPipeName + seq, ''); + return true; + } catch (e) { + // noop + } + } + if (this.trace !== Trace.Off) { this.logTrace(`TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.`); } diff --git a/extensions/typescript/src/utils/electron.ts b/extensions/typescript/src/utils/electron.ts index 916fe4faa61..799628d2801 100644 --- a/extensions/typescript/src/utils/electron.ts +++ b/extensions/typescript/src/utils/electron.ts @@ -17,7 +17,7 @@ export interface IForkOptions { execArgv?: string[]; } -function makeRandomHexString(length: number): string { +export function makeRandomHexString(length: number): string { let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; let result = ''; for (let i = 0; i < length; i++) { @@ -28,15 +28,20 @@ function makeRandomHexString(length: number): string { } function generatePipeName(): string { - var randomName = 'vscode-' + makeRandomHexString(40); + return getPipeName(makeRandomHexString(40)); +} + +export function getPipeName(name: string): string { + const fullName = 'vscode-' + name; if (process.platform === 'win32') { - return '\\\\.\\pipe\\' + randomName + '-sock'; + return '\\\\.\\pipe\\' + fullName + '-sock'; } // Mac/Unix: use socket file - return path.join(os.tmpdir(), randomName + '.sock'); + return path.join(os.tmpdir(), fullName + '.sock'); } + function generatePatchedEnv(env: any, stdInPipeName: string, stdOutPipeName: string, stdErrPipeName: string): any { // Set the two unique pipe names and the electron flag as process env diff --git a/package.json b/package.json index 6094d5485ec..cc71bee00f1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-oss-dev", "version": "1.11.0", "electronVersion": "1.4.6", - "distro": "64f6f295b56a3512bf34dfb643f770160fd33ee8", + "distro": "612d0710388e5aad15b430be26f86d86ddfd1abd", "author": { "name": "Microsoft Corporation" }, diff --git a/resources/linux/code.appdata.xml b/resources/linux/code.appdata.xml index 487a186b24f..ab9df8c25da 100644 --- a/resources/linux/code.appdata.xml +++ b/resources/linux/code.appdata.xml @@ -5,7 +5,7 @@ @@LICENSE@@ @@NAME_LONG@@ https://code.visualstudio.com - Code editing. Redefined. + Visual Studio Code. Code editing. Redefined.

Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ.

diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9f623833085..791f6ab7ab8 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -6,6 +6,7 @@ import 'vs/css!./list'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; +import { range } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import * as DOM from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; @@ -268,6 +269,10 @@ class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent) { + if (platform.isMacintosh ? e.altKey : e.ctrlKey) { + return this.onPointer(e); + } + e.preventDefault(); e.stopPropagation(); } @@ -278,6 +283,28 @@ class MouseController implements IDisposable { this.view.domNode.focus(); const focus = e.index; + + if (e.shiftKey) { + const oldFocus = this.list.getFocus()[0]; + + if (oldFocus !== undefined) { + const min = Math.min(oldFocus, focus); + const max = Math.max(oldFocus, focus); + const rangeSelection = range(max + 1, min); + const selection = this.list.getSelection(); + const contiguousRange = getContiguousRangeContaining(disjunction(selection, [oldFocus]), oldFocus); + + if (contiguousRange.length === 0) { + return; + } + + const newSelection = disjunction(rangeSelection, relativeComplement(selection, contiguousRange)); + this.list.setSelection(newSelection); + } + + return; + } + this.list.setFocus([focus]); if (platform.isMacintosh ? e.altKey : e.ctrlKey) { @@ -312,6 +339,56 @@ const DefaultOptions: IListOptions = { mouseSupport: true }; +function getContiguousRangeContaining(range: number[], value: number): number[] { + const index = range.indexOf(value); + + if (index === -1) { + return []; + } + + const result = []; + let i = index - 1; + while (i >= 0 && range[i] === value - (index - i)) { + result.push(range[i--]); + } + + result.reverse(); + i = index; + while (i < range.length && range[i] === value + (i - index)) { + result.push(range[i++]); + } + + return result; +} + +/** + * Given two sorted collections of numbers, returns the intersection + * betweem them (OR). + */ +function disjunction(one: number[], other: number[]): number[] { + const result = []; + let i = 0, j = 0; + + while (i < one.length || j < other.length) { + if (i >= one.length) { + result.push(other[j++]); + } else if (j >= other.length) { + result.push(one[i++]); + } else if (one[i] === other[j]) { + result.push(one[i]); + i++; + j++; + continue; + } else if (one[i] < other[j]) { + result.push(one[i++]); + } else { + result.push(other[j++]); + } + } + + return result; +} + /** * Given two sorted collections of numbers, returns the exclusive * disjunction between them (XOR). @@ -339,6 +416,33 @@ function exclusiveDisjunction(one: number[], other: number[]): number[] { return result; } +/** + * Given two sorted collections of numbers, returns the relative + * complement between them (XOR). + */ +function relativeComplement(one: number[], other: number[]): number[] { + const result = []; + let i = 0, j = 0; + + while (i < one.length || j < other.length) { + if (i >= one.length) { + result.push(other[j++]); + } else if (j >= other.length) { + result.push(one[i++]); + } else if (one[i] === other[j]) { + i++; + j++; + continue; + } else if (one[i] < other[j]) { + result.push(one[i++]); + } else { + j++; + } + } + + return result; +} + const numericSort = (a: number, b: number) => a - b; export class List implements ISpliceable, IDisposable { @@ -424,6 +528,7 @@ export class List implements ISpliceable, IDisposable { } this.onFocusChange(this._onFocusChange, this, this.disposables); + this.onSelectionChange(this._onSelectionChange, this, this.disposables); if (options.ariaLabel) { this.view.domNode.setAttribute('aria-label', options.ariaLabel); @@ -485,6 +590,10 @@ export class List implements ISpliceable, IDisposable { return this.selection.get(); } + getSelectedElements(): T[] { + return this.getSelection().map(i => this.view.element(i)); + } + setFocus(indexes: number[]): void { indexes = indexes.sort(numericSort); @@ -620,6 +729,14 @@ export class List implements ISpliceable, IDisposable { DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0); } + private _onSelectionChange(): void { + const selection = this.selection.get(); + + DOM.toggleClass(this.view.domNode, 'selection-none', selection.length === 0); + DOM.toggleClass(this.view.domNode, 'selection-single', selection.length === 1); + DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1); + } + dispose(): void { this._onDispose.fire(); this.disposables = dispose(this.disposables); diff --git a/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts b/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts index b5077f5ba33..ad13c1568b4 100644 --- a/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts +++ b/src/vs/base/browser/ui/resourceviewer/resourceViewer.ts @@ -15,8 +15,13 @@ import DOM = require('vs/base/browser/dom'); import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { BoundedLinkedMap } from 'vs/base/common/map'; + +interface MapExtToMediaMimes { + [index: string]: string; +} + // Known media mimes that we can handle -const mapExtToMediaMimes = { +const mapExtToMediaMimes: MapExtToMediaMimes = { '.bmp': 'image/bmp', '.gif': 'image/gif', '.jpg': 'image/jpg', diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 162172f2ad8..e6c146d1df3 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -226,10 +226,14 @@ export class ActionRunner extends EventEmitter implements IActionRunner { this.emit(Events.EventType.BEFORE_RUN, { action: action }); - return TPromise.as(action.run(context)).then((result: any) => { + return this.runAction(action, context).then((result: any) => { this.emit(Events.EventType.RUN, { action: action, result: result }); }, (error: any) => { this.emit(Events.EventType.RUN, { action: action, error: error }); }); } + + protected runAction(action: IAction, context?: any): TPromise { + return TPromise.as(action.run(context)); + } } diff --git a/src/vs/base/common/diagnostics.ts b/src/vs/base/common/diagnostics.ts index 9c0e4f6df8f..d967bb83013 100644 --- a/src/vs/base/common/diagnostics.ts +++ b/src/vs/base/common/diagnostics.ts @@ -18,7 +18,7 @@ if (!globals.Monaco) { globals.Monaco.Diagnostics = {}; var switches = globals.Monaco.Diagnostics; -var map = {}; +var map = new Map(); var data: any[] = []; function fifo(array: any[], size: number) { @@ -41,9 +41,9 @@ export function register(what: string, fn: Function): (...args: any[]) => void { switches[what] = flag; // register function - var tracers = map[what] || []; + var tracers = map.get(what) || []; tracers.push(fn); - map[what] = tracers; + map.set(what, tracers); var result = function (...args: any[]) { diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 5e16f0462dc..c7752a24779 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -401,6 +401,10 @@ export function fuzzyMatchAndScore(pattern: string, word: string): [number, numb return [-1, []]; } + if (pattern.length > word.length) { + return undefined; + } + let matches: number[] = []; let score = _matchRecursive( pattern, pattern.toLowerCase(), pattern.toUpperCase(), 0, @@ -440,6 +444,13 @@ export function _matchRecursive( return (pattern[patternPos] === word[wordPos] ? 17 : 11) + value; } + if ((idx = word.indexOf(upPattern[patternPos], wordPos)) >= 0 + && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0) + ) { + matches.unshift(idx); + return (pattern[patternPos] === word[idx] ? 17 : 11) + value; + } + if ((idx = lowWord.indexOf(`_${lowPatternChar}`, wordPos)) >= 0 && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 2, matches)) >= 0) ) { @@ -447,11 +458,11 @@ export function _matchRecursive( return (pattern[patternPos] === word[idx + 1] ? 17 : 11) + value; } - if ((idx = word.indexOf(upPattern[patternPos], wordPos)) >= 0 - && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0) + if ((idx = lowWord.indexOf(`.${lowPatternChar}`, wordPos)) >= 0 + && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 2, matches)) >= 0) ) { - matches.unshift(idx); - return (pattern[patternPos] === word[idx] ? 17 : 11) + value; + matches.unshift(idx + 1); + return 11 + value; } if (patternPos > 0 diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 3250af8282a..7197d9ed801 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -9,6 +9,7 @@ import strings = require('vs/base/common/strings'); import paths = require('vs/base/common/paths'); import { BoundedLinkedMap } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; +import { TPromise } from 'vs/base/common/winjs.base'; export interface IExpression { [pattern: string]: boolean | SiblingClause | any; @@ -218,14 +219,16 @@ const T4 = /^\*\*((\/[\w\.-]+)+)\/?$/; // **/something/else const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else export type ParsedPattern = (path: string, basename?: string) => boolean; -export type ParsedExpression = (path: string, basename?: string, siblingsFn?: () => string[]) => string /* the matching pattern */; + +// The ParsedExpression returns a Promise iff siblingsFn returns a Promise. +export type ParsedExpression = (path: string, basename?: string, siblingsFn?: () => string[] | TPromise) => string | TPromise /* the matching pattern */; export interface IGlobOptions { trimForExclusions?: boolean; } interface ParsedStringPattern { - (path: string, basename: string): string /* the matching pattern */; + (path: string, basename: string): string | TPromise /* the matching pattern */; basenames?: string[]; patterns?: string[]; allBasenames?: string[]; @@ -233,7 +236,7 @@ interface ParsedStringPattern { } type SiblingsPattern = { siblings: string[], name: string }; interface ParsedExpressionPattern { - (path: string, basename: string, siblingsPatternFn: () => SiblingsPattern): string /* the matching pattern */; + (path: string, basename: string, siblingsPatternFn: () => SiblingsPattern | TPromise): string | TPromise /* the matching pattern */; requiresSiblings?: boolean; allBasenames?: string[]; allPaths?: string[]; @@ -427,6 +430,16 @@ export function parse(arg1: string | IExpression, options: IGlobOptions = {}): a return parsedExpression(arg1, options); } +/** + * Same as `parse`, but the ParsedExpression is guaranteed to return a Promise + */ +export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression { + const parsedExpression = parse(expression, options); + return (path: string, basename?: string, siblingsFn?: () => TPromise): TPromise => { + return TPromise.as(parsedExpression(path, basename, siblingsFn)); + }; +} + export function getBasenameTerms(patternOrExpression: ParsedPattern | ParsedExpression): string[] { return (patternOrExpression).allBasenames || []; } @@ -475,23 +488,32 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string, siblingsFn?: () => string[]) { - let siblingsPattern: SiblingsPattern; + const resultExpression: ParsedStringPattern = function (path: string, basename: string, siblingsFn?: () => string[] | TPromise) { + let siblingsPattern: SiblingsPattern | TPromise; let siblingsResolved = !siblingsFn; + function siblingsToSiblingsPattern(siblings: string[]) { + if (siblings && siblings.length) { + if (!basename) { + basename = paths.basename(path); + } + const name = basename.substr(0, basename.length - paths.extname(path).length); + return { siblings, name }; + } + + return undefined; + } + function siblingsPatternFn() { // Resolve siblings only once if (!siblingsResolved) { siblingsResolved = true; const siblings = siblingsFn(); - if (siblings && siblings.length) { - if (!basename) { - basename = paths.basename(path); - } - const name = basename.substr(0, basename.length - paths.extname(path).length); - siblingsPattern = { siblings, name }; - } + siblingsPattern = TPromise.is(siblings) ? + siblings.then(siblingsToSiblingsPattern) : + siblingsToSiblingsPattern(siblings); } + return siblingsPattern; } @@ -538,7 +560,16 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio if (value) { const when = (value).when; if (typeof when === 'string') { - const result: ParsedExpressionPattern = function (path: string, basename: string, siblingsPatternFn: () => SiblingsPattern) { + const siblingsPatternToMatchingPattern = (siblingsPattern: SiblingsPattern): string => { + let clausePattern = when.replace('$(basename)', siblingsPattern.name); + if (siblingsPattern.siblings.indexOf(clausePattern) !== -1) { + return pattern; + } else { + return null; // pattern does not match in the end because the when clause is not satisfied + } + }; + + const result: ParsedExpressionPattern = (path: string, basename: string, siblingsPatternFn: () => SiblingsPattern | TPromise) => { if (!parsedPattern(path, basename)) { return null; } @@ -548,12 +579,9 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio return null; // pattern is malformed or we don't have siblings } - let clausePattern = when.replace('$(basename)', siblingsPattern.name); - if (siblingsPattern.siblings.indexOf(clausePattern) !== -1) { - return pattern; - } else { - return null; // pattern does not match in the end because the when clause is not satisfied - } + return TPromise.is(siblingsPattern) ? + siblingsPattern.then(siblingsPatternToMatchingPattern) : + siblingsPatternToMatchingPattern(siblingsPattern); }; result.requiresSiblings = true; return result; diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 9a3caf34ece..8dcc73fc332 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -58,12 +58,12 @@ function markedStringEqual(a: MarkedString, b: MarkedString): boolean { if (!a || !b) { return false; } - if (typeof a === 'string') { - return typeof b === 'string' && a === b; + if (typeof a === 'string' || typeof b === 'string') { + return typeof a === 'string' && typeof b === 'string' && a === b; } return ( - a['language'] === b['language'] - && a['value'] === b['value'] + a.language === b.language + && a.value === b.value ); } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 48b69f1ca13..7b1bab68961 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -972,7 +972,7 @@ export function getNodeValue(node: Node): any { if (node.type === 'array') { return node.children.map(getNodeValue); } else if (node.type === 'object') { - let obj = {}; + let obj: any = {}; for (let prop of node.children) { obj[prop.children[0].value] = getNodeValue(prop.children[1]); } diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 0af46c477fb..d4d74faadca 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -81,6 +81,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting } else if (parent.type === 'array' && typeof lastSegment === 'number') { let insertIndex = lastSegment; if (insertIndex === -1) { + // Insert let newProperty = `${JSON.stringify(value)}`; let edit: Edit; if (parent.children.length === 0) { @@ -91,7 +92,26 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting } return withFormatting(text, edit, formattingOptions); } else { - throw new Error('Array modification not supported yet'); + if (value === void 0 && parent.children.length >= 0) { + //Removal + let removalIndex = lastSegment; + let toRemove = parent.children[removalIndex]; + let edit: Edit; + if (parent.children.length === 1) { + // only item + edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; + } else if (parent.children.length - 1 === removalIndex) { + // last item + let previous = parent.children[removalIndex - 1]; + let offset = previous.offset + previous.length; + edit = { offset, length: parent.length - 2 - offset, content: '' }; + } else { + edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; + } + return withFormatting(text, edit, formattingOptions); + } else { + throw new Error('Array modification not supported yet'); + } } } else { throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`); diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index e1227fb2642..6b9dbb4c92b 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -45,7 +45,7 @@ export function getPathLabel(resource: URI | string, basePathProvider?: URI | st const basepath = basePathProvider && getPath(basePathProvider); - if (basepath && isEqualOrParent(absolutePath, basepath)) { + if (basepath && isEqualOrParent(absolutePath, basepath, !platform.isLinux /* ignorecase */)) { if (basepath === absolutePath) { return ''; // no label if pathes are identical } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 7b60bba2caa..7563aa5b98d 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -7,7 +7,6 @@ import URI from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { isLinux } from 'vs/base/common/platform'; export interface Key { toString(): string; @@ -305,7 +304,7 @@ export class LRUCache extends BoundedLinkedMap { class Node { element?: E; - readonly children = new Map(); + readonly children = new Map>(); } /** @@ -330,7 +329,7 @@ export class TrieMap { // find insertion node let node = this._root; for (; i < parts.length; i++) { - let child = node.children[parts[i]]; + let child = node.children.get(parts[i]); if (child) { node = child; continue; @@ -342,7 +341,7 @@ export class TrieMap { let newNode: Node; for (; i < parts.length; i++) { newNode = new Node(); - node.children[parts[i]] = newNode; + node.children.set(parts[i], newNode); node = newNode; } @@ -355,7 +354,7 @@ export class TrieMap { let { children } = this._root; let node: Node; for (const part of parts) { - node = children[part]; + node = children.get(part); if (!node) { return undefined; } @@ -371,7 +370,7 @@ export class TrieMap { let lastNode: Node; let { children } = this._root; for (const part of parts) { - const node = children[part]; + const node = children.get(part); if (!node) { break; } @@ -395,7 +394,7 @@ export class TrieMap { let { children } = this._root; let node: Node; for (const part of parts) { - node = children[part]; + node = children.get(part); if (!node) { return undefined; } @@ -411,7 +410,7 @@ export class TrieMap { export class ResourceMap { private map: Map; - constructor() { + constructor(private ignoreCase?: boolean) { this.map = new Map(); } @@ -455,14 +454,14 @@ export class ResourceMap { if (resource.scheme === Schemas.file) { key = resource.fsPath; - - if (!isLinux) { - key = key.toLowerCase(); - } } else { key = resource.toString(); } + if (this.ignoreCase) { + key = key.toLowerCase(); + } + return key; } } \ No newline at end of file diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index 3ad625b3653..1840d51950f 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -312,7 +312,7 @@ export function isUNC(path: string): boolean { } // Reference: https://en.wikipedia.org/wiki/Filename -const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; +const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[/]/g; const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; export function isValidBasename(name: string): boolean { if (!name || name.length === 0 || /^\s+$/.test(name)) { diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 9b4a3e07890..ff3068ff24e 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import platform = require('vs/base/common/platform'); +import * as platform from 'vs/base/common/platform'; function _encode(ch: string): string { @@ -152,7 +152,7 @@ export default class URI { return this; } - let {scheme, authority, path, query, fragment} = change; + let { scheme, authority, path, query, fragment } = change; if (scheme === void 0) { scheme = this.scheme; } else if (scheme === null) { @@ -217,8 +217,12 @@ export default class URI { const ret = new URI(); ret._scheme = 'file'; - // normalize to fwd-slashes - path = path.replace(/\\/g, URI._slash); + // normalize to fwd-slashes on windows, + // on other systems bwd-slaches are valid + // filename character, eg /f\oo/ba\r.txt + if (platform.isWindows) { + path = path.replace(/\\/g, URI._slash); + } // check for authority as used in UNC shares // or use the path as given @@ -325,7 +329,7 @@ export default class URI { const parts: string[] = []; - let {scheme, authority, path, query, fragment} = uri; + let { scheme, authority, path, query, fragment } = uri; if (scheme) { parts.push(scheme, ':'); } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index dc0c0c68e1e..f5eaa8eadc7 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -115,7 +115,7 @@ export class ViewItem implements IViewItem { public needsRender: boolean; public uri: string; public unbindDragStart: Lifecycle.IDisposable; - public loadingPromise: WinJS.Promise; + public loadingTimer: number; public _styles: any; private _draggable: boolean; @@ -815,10 +815,10 @@ export class TreeView extends HeightMap { var viewItem = this.items[item.id]; if (viewItem) { - viewItem.loadingPromise = WinJS.TPromise.timeout(TreeView.LOADING_DECORATION_DELAY).then(() => { - viewItem.loadingPromise = null; + viewItem.loadingTimer = setTimeout(() => { + viewItem.loadingTimer = 0; viewItem.loading = true; - }); + }, TreeView.LOADING_DECORATION_DELAY); } if (!e.isNested) { @@ -839,9 +839,9 @@ export class TreeView extends HeightMap { var viewItem = this.items[item.id]; if (viewItem) { - if (viewItem.loadingPromise) { - viewItem.loadingPromise.cancel(); - viewItem.loadingPromise = null; + if (viewItem.loadingTimer) { + clearTimeout(viewItem.loadingTimer); + viewItem.loadingTimer = 0; } viewItem.loading = false; diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index a9f118e80d2..9c50031560f 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -235,6 +235,8 @@ suite('Filters', () => { assertMatches('ccm', 'camelCasecm', '^camel^Casec^m', fuzzyMatchAndScore); assertMatches('myvable', 'myvariable', '^m^y^v^aria^b^l^e', fuzzyMatchAndScore); assertMatches('fdm', 'findModel', '^fin^d^Model', fuzzyMatchAndScore); + assertMatches('form', 'editor.formatOnSave', 'editor.^f^o^r^matOnSave', fuzzyMatchAndScore); + assertMatches('KeyboardLayoutEventChange=', 'KeyboardLayoutEventChange', undefined, fuzzyMatchAndScore); }); test('topScore', function () { diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index 9d16e2c6773..2b8ea381df2 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -131,4 +131,29 @@ suite('JSON - edits', () => { let edits = setProperty(content, [-1], 'bar', formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n "bar"\n]'); }); + + test('remove item in array with one item', () => { + let content = '[\n 1\n]'; + let edits = setProperty(content, [0], void 0, formatterOptions); + assertEdit(content, edits, '[]'); + }); + + test('remove item in the middle of the array', () => { + let content = '[\n 1,\n 2,\n 3\n]'; + let edits = setProperty(content, [1], void 0, formatterOptions); + assertEdit(content, edits, '[\n 1,\n 3\n]'); + }); + + test('remove last item in the array', () => { + let content = '[\n 1,\n 2,\n "bar"\n]'; + let edits = setProperty(content, [2], void 0, formatterOptions); + assertEdit(content, edits, '[\n 1,\n 2\n]'); + }); + + test('remove last item in the array if ends with comman', () => { + let content = '[\n 1,\n "foo",\n "bar",\n]'; + let edits = setProperty(content, [2], void 0, formatterOptions); + assertEdit(content, edits, '[\n 1,\n "foo"\n]'); + }); + }); \ No newline at end of file diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 19dc49c2d21..43cbc7b44b9 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -8,7 +8,6 @@ import { BoundedLinkedMap, LRUCache, LinkedMap, TrieMap, ResourceMap } from 'vs/base/common/map'; import * as assert from 'assert'; import URI from 'vs/base/common/uri'; -import { isLinux } from 'vs/base/common/platform'; suite('Map', () => { @@ -402,7 +401,7 @@ suite('Map', () => { assert.ok(map.has(resource2)); }); - test('ResourceMap - files', function () { + test('ResourceMap - files (do NOT ignorecase)', function () { const map = new ResourceMap(); const fileA = URI.parse('file://some/filea'); @@ -412,22 +411,43 @@ suite('Map', () => { map.set(fileA, 'true'); assert.equal(map.get(fileA), 'true'); - if (!isLinux) { - assert.equal(map.get(fileAUpper), 'true'); - } else { - assert.ok(!map.get(fileAUpper)); - } + assert.ok(!map.get(fileAUpper)); assert.ok(!map.get(fileB)); map.set(fileAUpper, 'false'); assert.equal(map.get(fileAUpper), 'false'); - if (!isLinux) { - assert.equal(map.get(fileA), 'false'); - } else { - assert.equal(map.get(fileA), 'true'); - } + assert.equal(map.get(fileA), 'true'); + + const windowsFile = URI.file('c:\\test with %25\\c#code'); + const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); + + map.set(windowsFile, 'true'); + map.set(uncFile, 'true'); + + assert.equal(map.get(windowsFile), 'true'); + assert.equal(map.get(uncFile), 'true'); + }); + + test('ResourceMap - files (ignorecase)', function () { + const map = new ResourceMap(true); + + const fileA = URI.parse('file://some/filea'); + const fileB = URI.parse('some://some/other/fileb'); + const fileAUpper = URI.parse('file://SOME/FILEA'); + + map.set(fileA, 'true'); + assert.equal(map.get(fileA), 'true'); + + assert.equal(map.get(fileAUpper), 'true'); + + assert.ok(!map.get(fileB)); + + map.set(fileAUpper, 'false'); + assert.equal(map.get(fileAUpper), 'false'); + + assert.equal(map.get(fileA), 'false'); const windowsFile = URI.file('c:\\test with %25\\c#code'); const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); diff --git a/src/vs/base/test/common/paths.test.ts b/src/vs/base/test/common/paths.test.ts index 230bfb306ed..789bf057604 100644 --- a/src/vs/base/test/common/paths.test.ts +++ b/src/vs/base/test/common/paths.test.ts @@ -187,9 +187,11 @@ suite('Paths', () => { assert.ok(!paths.isValidBasename('')); assert.ok(paths.isValidBasename('test.txt')); assert.ok(!paths.isValidBasename('/test.txt')); - assert.ok(!paths.isValidBasename('\\test.txt')); - if (platform.isWindows) { + if (!platform.isWindows) { + assert.ok(paths.isValidBasename('\\test.txt')); + } else if (platform.isWindows) { + assert.ok(!paths.isValidBasename('\\test.txt')); assert.ok(!paths.isValidBasename('aux')); assert.ok(!paths.isValidBasename('Aux')); assert.ok(!paths.isValidBasename('LPT0')); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index a80a276f70f..777182fffe7 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -7,6 +7,8 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { normalize } from 'vs/base/common/paths'; +import { isWindows } from 'vs/base/common/platform'; + suite('URI', () => { test('file#toString', () => { @@ -14,18 +16,36 @@ suite('URI', () => { assert.equal(URI.file('C:/win/path').toString(), 'file:///c%3A/win/path'); assert.equal(URI.file('c:/win/path/').toString(), 'file:///c%3A/win/path/'); assert.equal(URI.file('/c:/win/path').toString(), 'file:///c%3A/win/path'); - assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A/win/path'); - assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A/win/path'); }); - test('file#path', () => { - assert.equal(URI.file('c:/win/path').fsPath.replace(/\\/g, '/'), 'c:/win/path'); - assert.equal(URI.file('c:/win/path/').fsPath.replace(/\\/g, '/'), 'c:/win/path/'); - assert.equal(URI.file('C:/win/path').fsPath.replace(/\\/g, '/'), 'c:/win/path'); - assert.equal(URI.file('/c:/win/path').fsPath.replace(/\\/g, '/'), 'c:/win/path'); - assert.equal(URI.file('./c/win/path').fsPath.replace(/\\/g, '/'), '/./c/win/path'); - assert.equal(URI.file('c:\\win\\path').fsPath.replace(/\\/g, '/'), 'c:/win/path'); - assert.equal(URI.file('c:\\win/path').fsPath.replace(/\\/g, '/'), 'c:/win/path'); + test('URI.file (win-special)', () => { + if (isWindows) { + assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A/win/path'); + assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A/win/path'); + } else { + assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A%5Cwin%5Cpath'); + assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A%5Cwin/path'); + + } + }); + + test('file#fsPath (win-special)', () => { + if (isWindows) { + assert.equal(URI.file('c:\\win\\path').fsPath, 'c:\\win\\path'); + assert.equal(URI.file('c:\\win/path').fsPath, 'c:\\win\\path'); + + assert.equal(URI.file('c:/win/path').fsPath, 'c:\\win\\path'); + assert.equal(URI.file('c:/win/path/').fsPath, 'c:\\win\\path\\'); + assert.equal(URI.file('C:/win/path').fsPath, 'c:\\win\\path'); + assert.equal(URI.file('/c:/win/path').fsPath, 'c:\\win\\path'); + assert.equal(URI.file('./c/win/path').fsPath, '\\.\\c\\win\\path'); + } else { + assert.equal(URI.file('c:/win/path').fsPath, 'c:/win/path'); + assert.equal(URI.file('c:/win/path/').fsPath, 'c:/win/path/'); + assert.equal(URI.file('C:/win/path').fsPath, 'c:/win/path'); + assert.equal(URI.file('/c:/win/path').fsPath, 'c:/win/path'); + assert.equal(URI.file('./c/win/path').fsPath, '/./c/win/path'); + } }); test('URI#fsPath - no `fsPath` when no `path`', () => { @@ -244,69 +264,61 @@ suite('URI', () => { assert.throws(() => URI.parse('file:////shares/files/p.cs')); }); - test('URI#file', () => { + test('URI#file, win-speciale', () => { + if (isWindows) { + var value = URI.file('c:\\test\\drive'); + assert.equal(value.path, '/c:/test/drive'); + assert.equal(value.toString(), 'file:///c%3A/test/drive'); - var value = URI.file('\\\\shäres\\path\\c#\\plugin.json'); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, 'shäres'); - assert.equal(value.path, '/path/c#/plugin.json'); - assert.equal(value.fragment, ''); - assert.equal(value.query, ''); - assert.equal(value.toString(), 'file://sh%C3%A4res/path/c%23/plugin.json'); + value = URI.file('\\\\shäres\\path\\c#\\plugin.json'); + assert.equal(value.scheme, 'file'); + assert.equal(value.authority, 'shäres'); + assert.equal(value.path, '/path/c#/plugin.json'); + assert.equal(value.fragment, ''); + assert.equal(value.query, ''); + assert.equal(value.toString(), 'file://sh%C3%A4res/path/c%23/plugin.json'); - // identity toString -> parse -> toString - value = URI.parse(value.toString()); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, 'shäres'); - assert.equal(value.path, '/path/c#/plugin.json'); - assert.equal(value.fragment, ''); - assert.equal(value.query, ''); - assert.equal(value.toString(), 'file://sh%C3%A4res/path/c%23/plugin.json'); + value = URI.file('\\\\localhost\\c$\\GitDevelopment\\express'); + assert.equal(value.scheme, 'file'); + assert.equal(value.path, '/c$/GitDevelopment/express'); + assert.equal(value.fsPath, '\\\\localhost\\c$\\GitDevelopment\\express'); + assert.equal(value.query, ''); + assert.equal(value.fragment, ''); + assert.equal(value.toString(), 'file://localhost/c%24/GitDevelopment/express'); - value = URI.file('\\\\localhost\\c$\\GitDevelopment\\express'); - assert.equal(value.scheme, 'file'); - assert.equal(value.path, '/c$/GitDevelopment/express'); - assert.equal(value.fsPath, normalize('//localhost/c$/GitDevelopment/express', true)); - assert.equal(value.query, ''); - assert.equal(value.fragment, ''); - assert.equal(value.toString(), 'file://localhost/c%24/GitDevelopment/express'); + value = URI.file('c:\\test with %\\path'); + assert.equal(value.path, '/c:/test with %/path'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/path'); - value = URI.file('c:\\test with %\\path'); - assert.equal(value.path, '/c:/test with %/path'); - assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/path'); + value = URI.file('c:\\test with %25\\path'); + assert.equal(value.path, '/c:/test with %25/path'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/path'); - value = URI.file('c:\\test with %25\\path'); - assert.equal(value.path, '/c:/test with %25/path'); - assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/path'); + value = URI.file('c:\\test with %25\\c#code'); + assert.equal(value.path, '/c:/test with %25/c#code'); + assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/c%23code'); - value = URI.file('c:\\test with %25\\c#code'); - assert.equal(value.path, '/c:/test with %25/c#code'); - assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/c%23code'); + value = URI.file('\\\\shares'); + assert.equal(value.scheme, 'file'); + assert.equal(value.authority, 'shares'); + assert.equal(value.path, '/'); // slash is always there - value = URI.file('\\\\shares'); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, 'shares'); - assert.equal(value.path, '/'); // slash is always there + value = URI.file('\\\\shares\\'); + assert.equal(value.scheme, 'file'); + assert.equal(value.authority, 'shares'); + assert.equal(value.path, '/'); + } + }); - value = URI.file('\\\\shares\\'); - assert.equal(value.scheme, 'file'); - assert.equal(value.authority, 'shares'); - assert.equal(value.path, '/'); + test('URI#file, no path-is-uri check', () => { // we don't complain here - value = URI.file('file://path/to/file'); + let value = URI.file('file://path/to/file'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); assert.equal(value.path, '/file://path/to/file'); }); - test('URI#file, auto-slash windows drive letter', () => { - - var value = URI.file('c:\\test\\drive'); - assert.equal(value.path, '/c:/test/drive'); - assert.equal(value.toString(), 'file:///c%3A/test/drive'); - }); - test('URI#file, always slash', () => { var value = URI.file('a.file'); @@ -419,4 +431,4 @@ suite('URI', () => { // } // console.profileEnd(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/code/electron-browser/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts index e30c31a2f4b..048e1b743f2 100644 --- a/src/vs/code/electron-browser/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -63,10 +63,6 @@ function main(server: Server, initData: ISharedProcessInitData): void { instantiationService.invokeFunction(accessor => { const appenders: AppInsightsAppender[] = []; - if (product.aiConfig && product.aiConfig.key) { - appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.key)); - } - if (product.aiConfig && product.aiConfig.asimovKey) { appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 3cbdde6f0a0..011e2fae5ec 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -236,8 +236,8 @@ export class WindowsManager implements IWindowsMainService { // Send to windows if (target) { const otherWindowsWithTarget = WindowsManager.WINDOWS.filter(w => w.id !== windowId && typeof w.openedWorkspacePath === 'string'); - const directTargetMatch = otherWindowsWithTarget.filter(w => isEqual(target, w.openedWorkspacePath)); - const parentTargetMatch = otherWindowsWithTarget.filter(w => isParent(target, w.openedWorkspacePath)); + const directTargetMatch = otherWindowsWithTarget.filter(w => isEqual(target, w.openedWorkspacePath, !platform.isLinux /* ignorecase */)); + const parentTargetMatch = otherWindowsWithTarget.filter(w => isParent(target, w.openedWorkspacePath, !platform.isLinux /* ignorecase */)); const targetWindow = directTargetMatch.length ? directTargetMatch[0] : parentTargetMatch[0]; // prefer direct match over parent match if (targetWindow) { @@ -325,7 +325,7 @@ export class WindowsManager implements IWindowsMainService { // Any non extension host window with same workspace else if (!win.isExtensionDevelopmentHost && !!win.openedWorkspacePath) { this.windowsState.openedFolders.forEach(o => { - if (isEqual(o.workspacePath, win.openedWorkspacePath)) { + if (isEqual(o.workspacePath, win.openedWorkspacePath, !platform.isLinux /* ignorecase */)) { o.uiState = state.uiState; } }); @@ -518,7 +518,7 @@ export class WindowsManager implements IWindowsMainService { // Open remaining ones allFoldersToOpen.forEach(folderToOpen => { - if (windowsOnWorkspacePath.some(win => isEqual(win.openedWorkspacePath, folderToOpen))) { + if (windowsOnWorkspacePath.some(win => isEqual(win.openedWorkspacePath, folderToOpen, !platform.isLinux /* ignorecase */))) { return; // ignore folders that are already open } @@ -692,7 +692,7 @@ export class WindowsManager implements IWindowsMainService { // Reload an existing extension development host window on the same path // We currently do not allow more than one extension development window // on the same extension path. - let res = WindowsManager.WINDOWS.filter(w => w.config && isEqual(w.config.extensionDevelopmentPath, openConfig.cli.extensionDevelopmentPath)); + let res = WindowsManager.WINDOWS.filter(w => w.config && isEqual(w.config.extensionDevelopmentPath, openConfig.cli.extensionDevelopmentPath, !platform.isLinux /* ignorecase */)); if (res && res.length === 1) { this.reload(res[0], openConfig.cli); res[0].focus(); // make sure it gets focus and is restored @@ -914,7 +914,7 @@ export class WindowsManager implements IWindowsMainService { // Known Folder - load from stored settings if any if (configuration.workspacePath) { - const stateForWorkspace = this.windowsState.openedFolders.filter(o => isEqual(o.workspacePath, configuration.workspacePath)).map(o => o.uiState); + const stateForWorkspace = this.windowsState.openedFolders.filter(o => isEqual(o.workspacePath, configuration.workspacePath, !platform.isLinux /* ignorecase */)).map(o => o.uiState); if (stateForWorkspace.length) { return stateForWorkspace[0]; } @@ -1111,22 +1111,22 @@ export class WindowsManager implements IWindowsMainService { const res = windowsToTest.filter(w => { // match on workspace - if (typeof w.openedWorkspacePath === 'string' && (isEqual(w.openedWorkspacePath, workspacePath))) { + if (typeof w.openedWorkspacePath === 'string' && (isEqual(w.openedWorkspacePath, workspacePath, !platform.isLinux /* ignorecase */))) { return true; } // match on file - if (typeof w.openedFilePath === 'string' && isEqual(w.openedFilePath, filePath)) { + if (typeof w.openedFilePath === 'string' && isEqual(w.openedFilePath, filePath, !platform.isLinux /* ignorecase */)) { return true; } // match on file path - if (typeof w.openedWorkspacePath === 'string' && filePath && isEqualOrParent(filePath, w.openedWorkspacePath)) { + if (typeof w.openedWorkspacePath === 'string' && filePath && isEqualOrParent(filePath, w.openedWorkspacePath, !platform.isLinux /* ignorecase */)) { return true; } // match on extension development path - if (typeof extensionDevelopmentPath === 'string' && w.extensionDevelopmentPath === extensionDevelopmentPath) { + if (typeof extensionDevelopmentPath === 'string' && isEqual(w.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { return true; } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 68c64f1ae4c..38b0cc0f768 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -173,10 +173,6 @@ export function main(argv: ParsedArgs): TPromise { if (isBuilt && !extensionDevelopmentPath && product.enableTelemetry) { const appenders: AppInsightsAppender[] = []; - if (product.aiConfig && product.aiConfig.key) { - appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.key)); - } - if (product.aiConfig && product.aiConfig.asimovKey) { appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); } diff --git a/src/vs/code/node/windowsUtils.ts b/src/vs/code/node/windowsUtils.ts index d4f9403534a..31a4f00b354 100644 --- a/src/vs/code/node/windowsUtils.ts +++ b/src/vs/code/node/windowsUtils.ts @@ -47,7 +47,7 @@ export function findBestWindowOrFolder({ win } function findBestWindow(windows: WINDOW[], filePath: string): WINDOW { - const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && isEqualOrParent(filePath, window.openedWorkspacePath)); + const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && isEqualOrParent(filePath, window.openedWorkspacePath, !platform.isLinux /* ignorecase */)); if (containers.length) { return containers.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0]; } diff --git a/src/vs/editor/browser/editor.all.ts b/src/vs/editor/browser/editor.all.ts index 4d59420ef1d..0b200fdd95c 100644 --- a/src/vs/editor/browser/editor.all.ts +++ b/src/vs/editor/browser/editor.all.ts @@ -39,6 +39,5 @@ import 'vs/editor/contrib/snippet/common/snippet'; import 'vs/editor/contrib/snippet/browser/snippet'; import 'vs/editor/contrib/suggest/browser/suggestController'; import 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; -import 'vs/css!vs/editor/contrib/wordHighlighter/browser/wordHighlighter'; import 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; import 'vs/editor/contrib/folding/browser/folding'; diff --git a/src/vs/editor/browser/services/standaloneColorServiceImpl.ts b/src/vs/editor/browser/services/standaloneColorServiceImpl.ts deleted file mode 100644 index 1ff5a71d46a..00000000000 --- a/src/vs/editor/browser/services/standaloneColorServiceImpl.ts +++ /dev/null @@ -1,105 +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 { Theme, IThemeRule, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { IStandaloneColorService, BuiltinTheme, ITheme } from 'vs/editor/common/services/standaloneColorService'; -import { vs, vs_dark, hc_black } from 'vs/editor/common/standalone/themes'; -import * as dom from 'vs/base/browser/dom'; -import { TokenizationRegistry } from 'vs/editor/common/modes'; - -class KnownTheme { - cssClassName: string; - rules: IThemeRule[]; - - constructor(cssClassName: string, rules: IThemeRule[]) { - this.cssClassName = cssClassName; - this.rules = rules; - } -} - -const VS_THEME_NAME = 'vs'; -const VS_DARK_THEME_NAME = 'vs-dark'; -const HC_BLACK_THEME_NAME = 'hc-black'; - -function isBuiltinTheme(themeName: string): themeName is BuiltinTheme { - return ( - themeName === VS_THEME_NAME - || themeName === VS_DARK_THEME_NAME - || themeName === HC_BLACK_THEME_NAME - ); -} - -function getBuiltinRules(builtinTheme: BuiltinTheme): IThemeRule[] { - switch (builtinTheme) { - case VS_THEME_NAME: - return vs; - case VS_DARK_THEME_NAME: - return vs_dark; - case HC_BLACK_THEME_NAME: - return hc_black; - } -} - -export class StandaloneColorServiceImpl implements IStandaloneColorService { - - _serviceBrand: any; - - private _knownThemes: Map; - private _styleElement: HTMLStyleElement; - private _theme: Theme; - - constructor() { - this._knownThemes = new Map(); - this._knownThemes.set(VS_THEME_NAME, new KnownTheme(VS_THEME_NAME, getBuiltinRules(VS_THEME_NAME))); - this._knownThemes.set(VS_DARK_THEME_NAME, new KnownTheme(VS_DARK_THEME_NAME, getBuiltinRules(VS_DARK_THEME_NAME))); - this._knownThemes.set(HC_BLACK_THEME_NAME, new KnownTheme(HC_BLACK_THEME_NAME, getBuiltinRules(HC_BLACK_THEME_NAME))); - this._styleElement = dom.createStyleSheet(); - this._styleElement.className = 'monaco-tokens-styles'; - this.setTheme(VS_THEME_NAME); - } - - public defineTheme(themeName: string, themeData: ITheme): void { - if (!/^[a-z0-9\-]+$/i.test(themeName) || isBuiltinTheme(themeName)) { - throw new Error('Illegal theme name!'); - } - if (!isBuiltinTheme(themeData.base)) { - throw new Error('Illegal theme base!'); - } - - let cssClassName = themeData.base + ' ' + themeName; - - let rules: IThemeRule[] = []; - if (themeData.inherit) { - rules = rules.concat(getBuiltinRules(themeData.base)); - } - rules = rules.concat(themeData.rules); - - this._knownThemes.set(themeName, new KnownTheme(cssClassName, rules)); - } - - public getTheme(): Theme { - return this._theme; - } - - public setTheme(themeName: string): string { - let themeData: KnownTheme; - if (this._knownThemes.has(themeName)) { - themeData = this._knownThemes.get(themeName); - } else { - themeData = this._knownThemes.get(VS_THEME_NAME); - } - - - this._theme = Theme.createFromRawTheme(themeData.rules); - let colorMap = this._theme.getColorMap(); - let cssRules = generateTokensCSSForColorMap(colorMap); - this._styleElement.innerHTML = cssRules; - - TokenizationRegistry.setColorMap(colorMap); - - return themeData.cssClassName; - } -} diff --git a/src/vs/editor/browser/services/standaloneThemeServiceImpl.ts b/src/vs/editor/browser/services/standaloneThemeServiceImpl.ts new file mode 100644 index 00000000000..e94e0472872 --- /dev/null +++ b/src/vs/editor/browser/services/standaloneThemeServiceImpl.ts @@ -0,0 +1,205 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TokenTheme, ITokenThemeRule, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +import { IStandaloneThemeService, BuiltinTheme, IStandaloneThemeData, IStandaloneTheme, IColors } from 'vs/editor/common/services/standaloneThemeService'; +import { vs, vs_dark, hc_black } from 'vs/editor/common/standalone/themes'; +import * as dom from 'vs/base/browser/dom'; +import { TokenizationRegistry } from 'vs/editor/common/modes'; +import { Color } from 'vs/base/common/color'; +import { Extensions, IColorRegistry, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { Extensions as ThemingExtensions, IThemingRegistry, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { Registry } from 'vs/platform/platform'; +import Event, { Emitter } from 'vs/base/common/event'; + +const VS_THEME_NAME = 'vs'; +const VS_DARK_THEME_NAME = 'vs-dark'; +const HC_BLACK_THEME_NAME = 'hc-black'; + +const colorRegistry = Registry.as(Extensions.ColorContribution); +const themingRegistry = Registry.as(ThemingExtensions.ThemingContribution); + +class StandaloneTheme implements IStandaloneTheme { + id: string; + selector: string; + private rules: ITokenThemeRule[]; + base: string; + private colors: { [colorId: string]: Color }; + private defaultColors: { [colorId: string]: Color }; + private _tokenTheme: TokenTheme; + + constructor(base: string, name: string, colors: IColors, rules: ITokenThemeRule[]) { + if (name.length > 0) { + this.id = base + ' ' + name; + this.selector = base + '.' + name; + } else { + this.id = base; + this.selector = base; + } + this.rules = rules; + this.colors = {}; + for (let id in colors) { + this.colors[id] = Color.fromHex(colors[id]); + } + this.defaultColors = {}; + } + + public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color { + if (this.colors.hasOwnProperty(colorId)) { + return this.colors[colorId]; + } + if (useDefault !== false) { + return this.getDefault(colorId); + } + return null; + } + + private getDefault(colorId: ColorIdentifier): Color { + if (this.defaultColors.hasOwnProperty(colorId)) { + return this.defaultColors[colorId]; + } + let color = colorRegistry.resolveDefaultColor(colorId, this); + this.defaultColors[colorId] = color; + return color; + } + + public isDefault(colorId: ColorIdentifier): boolean { + if (!this.colors.hasOwnProperty(colorId)) { + return true; + } + let color = this.colors[colorId]; + let defaultValue = this.getDefault(colorId); + return color ? !!defaultValue : color.equals(defaultValue); + } + + public get type() { + switch (this.base) { + case VS_THEME_NAME: return 'light'; + case HC_BLACK_THEME_NAME: return 'hc'; + default: return 'dark'; + } + } + + public get tokenTheme(): TokenTheme { + if (!this._tokenTheme) { + this._tokenTheme = TokenTheme.createFromRawTokenTheme(this.rules); + } + return this._tokenTheme; + } +} + +function isBuiltinTheme(themeName: string): themeName is BuiltinTheme { + return ( + themeName === VS_THEME_NAME + || themeName === VS_DARK_THEME_NAME + || themeName === HC_BLACK_THEME_NAME + ); +} + +function getBuiltinRules(builtinTheme: BuiltinTheme): IStandaloneThemeData { + switch (builtinTheme) { + case VS_THEME_NAME: + return vs; + case VS_DARK_THEME_NAME: + return vs_dark; + case HC_BLACK_THEME_NAME: + return hc_black; + } +} + +function newBuiltInTheme(builtinTheme: BuiltinTheme): StandaloneTheme { + let themeData = getBuiltinRules(builtinTheme); + return new StandaloneTheme(builtinTheme, '', themeData.colors, themeData.rules); +} + +export class StandaloneThemeServiceImpl implements IStandaloneThemeService { + + _serviceBrand: any; + + private _knownThemes: Map; + private _styleElement: HTMLStyleElement; + private _theme: IStandaloneTheme; + private _onThemeChange: Emitter; + + + constructor() { + this._onThemeChange = new Emitter(); + + this._knownThemes = new Map(); + this._knownThemes.set(VS_THEME_NAME, newBuiltInTheme(VS_THEME_NAME)); + this._knownThemes.set(VS_DARK_THEME_NAME, newBuiltInTheme(VS_DARK_THEME_NAME)); + this._knownThemes.set(HC_BLACK_THEME_NAME, newBuiltInTheme(HC_BLACK_THEME_NAME)); + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'monaco-colors'; + this.setTheme(VS_THEME_NAME); + } + + public get onThemeChange(): Event { + return this._onThemeChange.event; + } + + public defineTheme(themeName: string, themeData: IStandaloneThemeData): void { + if (!/^[a-z0-9\-]+$/i.test(themeName) || isBuiltinTheme(themeName)) { + throw new Error('Illegal theme name!'); + } + if (!isBuiltinTheme(themeData.base)) { + throw new Error('Illegal theme base!'); + } + + let rules: ITokenThemeRule[] = []; + let colors: IColors = {}; + if (themeData.inherit) { + let baseData = getBuiltinRules(themeData.base); + rules = rules.concat(baseData.rules); + for (let id in baseData.colors) { + colors[id] = baseData.colors[id]; + } + } + rules = rules.concat(themeData.rules); + for (let id in themeData.colors) { + colors[id] = themeData.colors[id]; + } + + this._knownThemes.set(themeName, new StandaloneTheme(themeData.base, themeName, colors, rules)); + } + + public getTheme(): IStandaloneTheme { + return this._theme; + } + + public setTheme(themeName: string): string { + let theme: StandaloneTheme; + if (this._knownThemes.has(themeName)) { + theme = this._knownThemes.get(themeName); + } else { + theme = this._knownThemes.get(VS_THEME_NAME); + } + this._theme = theme; + + let cssRules = []; + let hasRule = {}; + let ruleCollector: ICssStyleCollector = { + addRule: (rule: string) => { + if (!hasRule[rule]) { + cssRules.push(rule); + hasRule[rule] = true; + } + } + }; + themingRegistry.getThemingParticipants().forEach(p => p(theme, ruleCollector)); + + let tokenTheme = theme.tokenTheme; + let colorMap = tokenTheme.getColorMap(); + ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); + + this._styleElement.innerHTML = cssRules.join('\n'); + + TokenizationRegistry.setColorMap(colorMap); + this._onThemeChange.fire(theme); + + return theme.id; + } +} diff --git a/src/vs/editor/browser/standalone/colorizer.ts b/src/vs/editor/browser/standalone/colorizer.ts index e3dd5e34cf3..f6ee43ac3c3 100644 --- a/src/vs/editor/browser/standalone/colorizer.ts +++ b/src/vs/editor/browser/standalone/colorizer.ts @@ -13,7 +13,7 @@ import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/vie import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import * as strings from 'vs/base/common/strings'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; export interface IColorizerOptions { tabSize?: number; @@ -26,7 +26,7 @@ export interface IColorizerElementOptions extends IColorizerOptions { export class Colorizer { - public static colorizeElement(standaloneColorService: IStandaloneColorService, modeService: IModeService, domNode: HTMLElement, options: IColorizerElementOptions): TPromise { + public static colorizeElement(themeService: IStandaloneThemeService, modeService: IModeService, domNode: HTMLElement, options: IColorizerElementOptions): TPromise { options = options || {}; let theme = options.theme || 'vs'; let mimeType = options.mimeType || domNode.getAttribute('lang') || domNode.getAttribute('data-lang'); @@ -35,7 +35,7 @@ export class Colorizer { return undefined; } - standaloneColorService.setTheme(theme); + themeService.setTheme(theme); let text = domNode.firstChild.nodeValue; domNode.className += 'monaco-editor ' + theme; diff --git a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts index 7e26784cc0e..9fe1a25c9b5 100644 --- a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts @@ -20,7 +20,7 @@ import { IEditorContextViewService } from 'vs/editor/browser/standalone/standalo import { CodeEditor } from 'vs/editor/browser/codeEditor'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; @@ -185,7 +185,7 @@ export class StandaloneCodeEditor extends CodeEditor implements IStandaloneCodeE export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor { private _contextViewService: IEditorContextViewService; - private _standaloneColorService: IStandaloneColorService; + private _standaloneThemeService: IStandaloneThemeService; private _ownsModel: boolean; private _toDispose2: IDisposable[]; @@ -199,17 +199,17 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @IContextViewService contextViewService: IContextViewService, - @IStandaloneColorService standaloneColorService: IStandaloneColorService + @IStandaloneThemeService standaloneThemeService: IStandaloneThemeService ) { options = options || {}; if (typeof options.theme === 'string') { - options.theme = standaloneColorService.setTheme(options.theme); + options.theme = standaloneThemeService.setTheme(options.theme); } super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, keybindingService); this._contextViewService = contextViewService; - this._standaloneColorService = standaloneColorService; + this._standaloneThemeService = standaloneThemeService; this._toDispose2 = [toDispose]; let model: IModel = null; @@ -243,7 +243,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon public updateOptions(newOptions: IEditorOptions): void { if (typeof newOptions.theme === 'string') { - newOptions.theme = this._standaloneColorService.setTheme(newOptions.theme); + newOptions.theme = this._standaloneThemeService.setTheme(newOptions.theme); } super.updateOptions(newOptions); } @@ -267,7 +267,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { private _contextViewService: IEditorContextViewService; - private _standaloneColorService: IStandaloneColorService; + private _standaloneThemeService: IStandaloneThemeService; private _standaloneKeybindingService: StandaloneKeybindingService; private _toDispose2: IDisposable[]; @@ -279,7 +279,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @IContextViewService contextViewService: IContextViewService, - @IStandaloneColorService standaloneColorService: IStandaloneColorService, + @IStandaloneThemeService standaloneColorService: IStandaloneThemeService, @IEditorWorkerService editorWorkerService: IEditorWorkerService ) { options = options || {}; @@ -294,7 +294,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon } this._contextViewService = contextViewService; - this._standaloneColorService = standaloneColorService; + this._standaloneThemeService = standaloneColorService; this._toDispose2 = [toDispose]; @@ -312,7 +312,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon public updateOptions(newOptions: IEditorOptions): void { if (typeof newOptions.theme === 'string') { - newOptions.theme = this._standaloneColorService.setTheme(newOptions.theme); + newOptions.theme = this._standaloneThemeService.setTheme(newOptions.theme); } super.updateOptions(newOptions); } diff --git a/src/vs/editor/browser/standalone/standaloneEditor.ts b/src/vs/editor/browser/standalone/standaloneEditor.ts index 92f09cab031..6478d3c8ffa 100644 --- a/src/vs/editor/browser/standalone/standaloneEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneEditor.ts @@ -31,7 +31,7 @@ import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService' import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; -import { ITheme, IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; +import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; import { Token } from 'vs/editor/common/core/token'; import { FontInfo, BareFontInfo } from 'vs/editor/common/config/fontInfo'; @@ -92,7 +92,7 @@ export function create(domElement: HTMLElement, options?: IEditorConstructionOpt services.get(IContextKeyService), services.get(IKeybindingService), services.get(IContextViewService), - services.get(IStandaloneColorService) + services.get(IStandaloneThemeService) ); }); } @@ -123,7 +123,7 @@ export function createDiffEditor(domElement: HTMLElement, options?: IDiffEditorC services.get(IContextKeyService), services.get(IKeybindingService), services.get(IContextViewService), - services.get(IStandaloneColorService), + services.get(IStandaloneThemeService), services.get(IEditorWorkerService) ); }); @@ -244,7 +244,7 @@ export function createWebWorker(opts: IWebWorkerOptions): MonacoWebWorker * Colorize the contents of `domNode` using attribute `data-lang`. */ export function colorizeElement(domNode: HTMLElement, options: IColorizerElementOptions): TPromise { - return Colorizer.colorizeElement(StaticServices.standaloneColorService.get(), StaticServices.modeService.get(), domNode, options); + return Colorizer.colorizeElement(StaticServices.standaloneThemeService.get(), StaticServices.modeService.get(), domNode, options); } /** @@ -301,8 +301,8 @@ export function tokenize(text: string, languageId: string): Token[][] { /** * Define a new theme. */ -export function defineTheme(themeName: string, themeData: ITheme): void { - StaticServices.standaloneColorService.get().defineTheme(themeName, themeData); +export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void { + StaticServices.standaloneThemeService.get().defineTheme(themeName, themeData); } /** diff --git a/src/vs/editor/browser/standalone/standaloneLanguages.ts b/src/vs/editor/browser/standalone/standaloneLanguages.ts index 3a6bf37eabe..fc0fb3370e2 100644 --- a/src/vs/editor/browser/standalone/standaloneLanguages.ts +++ b/src/vs/editor/browser/standalone/standaloneLanguages.ts @@ -23,7 +23,7 @@ import { createTokenizationSupport } from 'vs/editor/common/modes/monarch/monarc import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; /** * Register information about a new language. @@ -73,12 +73,12 @@ export function setLanguageConfiguration(languageId: string, configuration: Lang */ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { - private readonly _standaloneColorService: IStandaloneColorService; + private readonly _standaloneThemeService: IStandaloneThemeService; private readonly _languageIdentifier: modes.LanguageIdentifier; private readonly _actual: TokensProvider; - constructor(standaloneColorService: IStandaloneColorService, languageIdentifier: modes.LanguageIdentifier, actual: TokensProvider) { - this._standaloneColorService = standaloneColorService; + constructor(standaloneThemeService: IStandaloneThemeService, languageIdentifier: modes.LanguageIdentifier, actual: TokensProvider) { + this._standaloneThemeService = standaloneThemeService; this._languageIdentifier = languageIdentifier; this._actual = actual; } @@ -113,12 +113,12 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { private _toBinaryTokens(tokens: IToken[], offsetDelta: number): Uint32Array { let languageId = this._languageIdentifier.id; - let theme = this._standaloneColorService.getTheme(); + let tokenTheme = this._standaloneThemeService.getTheme().tokenTheme; let result: number[] = [], resultLen = 0; for (let i = 0, len = tokens.length; i < len; i++) { let t = tokens[i]; - let metadata = theme.match(languageId, t.scopes); + let metadata = tokenTheme.match(languageId, t.scopes); if (resultLen > 0 && result[resultLen - 1] === metadata) { // same metadata continue; @@ -195,7 +195,7 @@ export function setTokensProvider(languageId: string, provider: TokensProvider): if (!languageIdentifier) { throw new Error(`Cannot set tokens provider for unknown language ${languageId}`); } - let adapter = new TokenizationSupport2Adapter(StaticServices.standaloneColorService.get(), languageIdentifier, provider); + let adapter = new TokenizationSupport2Adapter(StaticServices.standaloneThemeService.get(), languageIdentifier, provider); return modes.TokenizationRegistry.register(languageId, adapter); } @@ -204,7 +204,7 @@ export function setTokensProvider(languageId: string, provider: TokensProvider): */ export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage): IDisposable { let lexer = compile(languageId, languageDef); - let adapter = createTokenizationSupport(StaticServices.modeService.get(), StaticServices.standaloneColorService.get(), languageId, lexer); + let adapter = createTokenizationSupport(StaticServices.modeService.get(), StaticServices.standaloneThemeService.get(), languageId, lexer); return modes.TokenizationRegistry.register(languageId, adapter); } diff --git a/src/vs/editor/browser/standalone/standaloneServices.ts b/src/vs/editor/browser/standalone/standaloneServices.ts index fbe6b0ea7ba..d3354077518 100644 --- a/src/vs/editor/browser/standalone/standaloneServices.ts +++ b/src/vs/editor/browser/standalone/standaloneServices.ts @@ -38,8 +38,8 @@ import { } from 'vs/editor/browser/standalone/simpleServices'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IMenuService } from 'vs/platform/actions/common/actions'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; -import { StandaloneColorServiceImpl } from 'vs/editor/browser/services/standaloneColorServiceImpl'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; +import { StandaloneThemeServiceImpl } from 'vs/editor/browser/services/standaloneThemeServiceImpl'; export interface IEditorContextViewService extends IContextViewService { dispose(): void; @@ -47,6 +47,7 @@ export interface IEditorContextViewService extends IContextViewService { } export interface IEditorOverrideServices { + [index: string]: any; } export module StaticServices { @@ -138,7 +139,7 @@ export module StaticServices { export const storageService = define(IStorageService, () => NullStorageService); - export const standaloneColorService = define(IStandaloneColorService, () => new StandaloneColorServiceImpl()); + export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl()); } export class DynamicStandaloneServices extends Disposable { diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css index f9575e6b526..80ec19b898c 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.css @@ -9,16 +9,4 @@ left: 0; top: 0; box-sizing: border-box; -} - -.monaco-editor.vs .view-overlays .current-line { - border: 2px solid #eee; -} - -.monaco-editor.vs-dark .view-overlays .current-line { - border: 2px solid #282828; -} - -.monaco-editor.hc-black .view-overlays .current-line { - border: 2px solid #f38518; -} +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css index 12817520df5..ec7e4b710c6 100644 --- a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.css @@ -9,16 +9,4 @@ left: 0; top: 0; box-sizing: border-box; -} - -.monaco-editor.vs .margin-view-overlays .current-line-margin { - border: 2px solid #eee; -} - -.monaco-editor.vs-dark .margin-view-overlays .current-line-margin { - border: 2px solid #282828; -} - -.monaco-editor.hc-black .margin-view-overlays .current-line-margin { - border: 2px solid #f38518; -} +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css index 3df1dce6bb7..c05287861d9 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css @@ -8,10 +8,6 @@ top: 0; } -.monaco-editor.vs .glyph-margin { background: white; } -.monaco-editor.vs-dark .glyph-margin { background: #1E1E1E; } -.monaco-editor.hc-black .glyph-margin { background: #000; } - /* Keeping name short for faster parsing. cgmr = core glyph margin rendering (div) diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css index a4793a66044..7cd5f89fd9f 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css @@ -9,14 +9,5 @@ */ .monaco-editor .lines-content .cigr { position: absolute; - background: lightgray; width: 1px; } - -.monaco-editor.vs-dark .lines-content .cigr { - background: #404040; -} - -.monaco-editor.hc-black .lines-content .cigr { - background: #ffffff; -} diff --git a/src/vs/editor/browser/viewParts/selections/selections.css b/src/vs/editor/browser/viewParts/selections/selections.css index e7630a592a9..28b077f995d 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.css +++ b/src/vs/editor/browser/viewParts/selections/selections.css @@ -21,11 +21,5 @@ .monaco-editor.hc-black .top-right-radius { border-top-right-radius: 0; } .monaco-editor.hc-black .bottom-right-radius { border-bottom-right-radius: 0; } -.monaco-editor.vs .view-overlays .selected-text { background: #E5EBF1; } -.monaco-editor.vs .view-overlays.focused .selected-text { background: #ADD6FF; } -.monaco-editor.vs-dark .view-overlays .selected-text { background: #3A3D41; } -.monaco-editor.vs-dark .view-overlays.focused .selected-text { background: #264F78; } -.monaco-editor.hc-black .view-overlays .selected-text { background: none; } -.monaco-editor.hc-black .view-overlays.focused .selected-text { background: #f3f518; } .monaco-editor.ie.hc-black .view-overlays.focused .selected-text { background: none; border: 2px solid #f38518; } .monaco-editor.edge.hc-black .view-overlays.focused .selected-text { background: none; border: 2px solid #f38518; } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css index 2da05d2fb0c..b7bb12838c5 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -39,26 +39,6 @@ box-sizing: border-box; } -.monaco-editor.vs .cursor { - background: black; - border-color: black; - color: white; /* opposite of black */ -} -.monaco-editor.vs-dark .cursor { - background: #AEAFAD; - border-color: #AEAFAD; - color: #51504f; /* opposite of #AEAFAD */ -} -.monaco-editor.hc-black .cursor { - background: #fff; - border-color: #fff; - color: #000; /* opposite of #fff */ -} -.monaco-editor.hc-black .cursors-layer.has-selection .cursor { - border-left: 1px solid #000; - border-right: 1px solid #000; -} - @keyframes monaco-cursor-blink { 50% { opacity: 0; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index b10bf4c0280..9a5d1c7f24f 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -334,5 +334,9 @@ registerThemingParticipant((theme, collector) => { if (caret) { let oppositeCaret = caret.opposite(); collector.addRule(`.monaco-editor.${theme.selector} .cursor { background-color: ${caret}; border-color: ${caret}; color: ${oppositeCaret}; }`); + if (theme.type === 'hc') { + collector.addRule(`.monaco-editor.${theme.selector} .cursors-layer.has-selection .cursor { border-left: 1px solid ${oppositeCaret}; border-right: 1px solid ${oppositeCaret}; }`); + } } + }); \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/editor.css b/src/vs/editor/browser/widget/media/editor.css index b665708cea7..90a691620e8 100644 --- a/src/vs/editor/browser/widget/media/editor.css +++ b/src/vs/editor/browser/widget/media/editor.css @@ -71,9 +71,6 @@ */ background: #fffffe; } -.monaco-editor-background { - background: #fffffe; -} .monaco-editor.ime-input .inputarea { background: rgba(255, 255, 255, 0.85); } @@ -84,9 +81,6 @@ color: #BBB; background: #1E1E1E; } -.monaco-editor.vs-dark .monaco-editor-background { - background: #1E1E1E; -} .monaco-editor.vs-dark.ime-input .inputarea { background: rgba(0, 0, 0, 0.65); } @@ -97,9 +91,6 @@ color: #fff; background: #000; } -.monaco-editor.hc-black .monaco-editor-background { - background: #000; -} /* -------------------- Misc -------------------- */ @@ -114,12 +105,6 @@ top: 0; } -/* -------------------- Highlight a range -------------------- */ - -.monaco-editor.vs .rangeHighlight { background: rgba(253, 255, 0, 0.2); } -.monaco-editor.vs-dark .rangeHighlight { background: rgba(255, 255, 255, 0.044); } -.monaco-editor.hc-black .rangeHighlight { border: 1px dotted #f38518; } - /* -------------------- Squigglies -------------------- */ .monaco-editor.vs .redsquiggly, diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index c06f00974e3..5df073ba886 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -189,6 +189,7 @@ export type SuggestionType = 'method' | 'unit' | 'value' | 'enum' + | 'enum-member' | 'keyword' | 'snippet' | 'text' diff --git a/src/vs/editor/common/modes/monarch/monarchLexer.ts b/src/vs/editor/common/modes/monarch/monarchLexer.ts index 69c65946c78..fd57abca71a 100644 --- a/src/vs/editor/common/modes/monarch/monarchLexer.ts +++ b/src/vs/editor/common/modes/monarch/monarchLexer.ts @@ -15,8 +15,8 @@ import * as monarchCommon from 'vs/editor/common/modes/monarch/monarchCommon'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Token, TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; import { NULL_STATE, NULL_MODE_ID } from 'vs/editor/common/modes/nullMode'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; -import { Theme } from 'vs/editor/common/modes/supports/tokenization'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; +import { TokenTheme } from 'vs/editor/common/modes/supports/tokenization'; const CACHE_STACK_DEPTH = 5; @@ -291,13 +291,13 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector { class MonarchModernTokensCollector implements IMonarchTokensCollector { private _modeService: IModeService; - private _theme: Theme; + private _theme: TokenTheme; private _prependTokens: Uint32Array; private _tokens: number[]; private _currentLanguageId: modes.LanguageId; private _lastTokenMetadata: number; - constructor(modeService: IModeService, theme: Theme) { + constructor(modeService: IModeService, theme: TokenTheme) { this._modeService = modeService; this._theme = theme; this._prependTokens = null; @@ -378,15 +378,15 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { export class MonarchTokenizer implements modes.ITokenizationSupport { private readonly _modeService: IModeService; - private readonly _standaloneColorService: IStandaloneColorService; + private readonly _standaloneThemeService: IStandaloneThemeService; private readonly _modeId: string; private readonly _lexer: monarchCommon.ILexer; private _embeddedModes: { [modeId: string]: boolean; }; private _tokenizationRegistryListener: IDisposable; - constructor(modeService: IModeService, standaloneColorService: IStandaloneColorService, modeId: string, lexer: monarchCommon.ILexer) { + constructor(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer) { this._modeService = modeService; - this._standaloneColorService = standaloneColorService; + this._standaloneThemeService = standaloneThemeService; this._modeId = modeId; this._lexer = lexer; this._embeddedModes = Object.create(null); @@ -429,7 +429,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { } public tokenize2(line: string, lineState: modes.IState, offsetDelta: number): TokenizationResult2 { - let tokensCollector = new MonarchModernTokensCollector(this._modeService, this._standaloneColorService.getTheme()); + let tokensCollector = new MonarchModernTokensCollector(this._modeService, this._standaloneThemeService.getTheme().tokenTheme); let endLineState = this._tokenize(line, lineState, offsetDelta, tokensCollector); return tokensCollector.finalize(endLineState); } @@ -836,6 +836,6 @@ function findBracket(lexer: monarchCommon.ILexer, matched: string) { return null; } -export function createTokenizationSupport(modeService: IModeService, standaloneColorService: IStandaloneColorService, modeId: string, lexer: monarchCommon.ILexer): modes.ITokenizationSupport { - return new MonarchTokenizer(modeService, standaloneColorService, modeId, lexer); +export function createTokenizationSupport(modeService: IModeService, standaloneThemeService: IStandaloneThemeService, modeId: string, lexer: monarchCommon.ILexer): modes.ITokenizationSupport { + return new MonarchTokenizer(modeService, standaloneThemeService, modeId, lexer); } diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index bf5025ee085..55851cc295f 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -7,14 +7,14 @@ import { ColorId, FontStyle, MetadataConsts, LanguageId, StandardTokenType } from 'vs/editor/common/modes'; import { Color } from 'vs/base/common/color'; -export interface IThemeRule { +export interface ITokenThemeRule { token: string; foreground?: string; background?: string; fontStyle?: string; } -export class ParsedThemeRule { +export class ParsedTokenThemeRule { _parsedThemeRuleBrand: void; readonly token: string; @@ -45,11 +45,11 @@ export class ParsedThemeRule { /** * Parse a raw theme into rules. */ -export function parseTheme(source: IThemeRule[]): ParsedThemeRule[] { +export function parseTokenTheme(source: ITokenThemeRule[]): ParsedTokenThemeRule[] { if (!source || !Array.isArray(source)) { return []; } - let result: ParsedThemeRule[] = [], resultLen = 0; + let result: ParsedTokenThemeRule[] = [], resultLen = 0; for (let i = 0, len = source.length; i < len; i++) { let entry = source[i]; @@ -84,7 +84,7 @@ export function parseTheme(source: IThemeRule[]): ParsedThemeRule[] { background = entry.background; } - result[resultLen++] = new ParsedThemeRule( + result[resultLen++] = new ParsedTokenThemeRule( entry.token || '', i, fontStyle, @@ -99,7 +99,7 @@ export function parseTheme(source: IThemeRule[]): ParsedThemeRule[] { /** * Resolve rules (i.e. inheritance). */ -function resolveParsedThemeRules(parsedThemeRules: ParsedThemeRule[]): Theme { +function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[]): TokenTheme { // Sort rules lexicographically, and then by index if necessary parsedThemeRules.sort((a, b) => { @@ -136,7 +136,7 @@ function resolveParsedThemeRules(parsedThemeRules: ParsedThemeRule[]): Theme { root.insert(rule.token, rule.fontStyle, colorMap.getId(rule.foreground), colorMap.getId(rule.background)); } - return new Theme(colorMap, root); + return new TokenTheme(colorMap, root); } export class ColorMap { @@ -175,14 +175,14 @@ export class ColorMap { } -export class Theme { +export class TokenTheme { - public static createFromRawTheme(source: IThemeRule[]): Theme { - return this.createFromParsedTheme(parseTheme(source)); + public static createFromRawTokenTheme(source: ITokenThemeRule[]): TokenTheme { + return this.createFromParsedTokenTheme(parseTokenTheme(source)); } - public static createFromParsedTheme(source: ParsedThemeRule[]): Theme { - return resolveParsedThemeRules(source); + public static createFromParsedTokenTheme(source: ParsedTokenThemeRule[]): TokenTheme { + return resolveParsedTokenThemeRules(source); } private readonly _colorMap: ColorMap; diff --git a/src/vs/editor/common/services/standaloneColorService.ts b/src/vs/editor/common/services/standaloneColorService.ts deleted file mode 100644 index 2b0b9a9f7a3..00000000000 --- a/src/vs/editor/common/services/standaloneColorService.ts +++ /dev/null @@ -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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Theme, IThemeRule } from 'vs/editor/common/modes/supports/tokenization'; - -export var IStandaloneColorService = createDecorator('standaloneColorService'); - -export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black'; - -export interface ITheme { - base: BuiltinTheme; - inherit: boolean; - rules: IThemeRule[]; -} - -export interface IStandaloneColorService { - _serviceBrand: any; - - setTheme(themeName: string): string; - - defineTheme(themeName: string, themeData: ITheme): void; - - getTheme(): Theme; -} diff --git a/src/vs/editor/common/services/standaloneThemeService.ts b/src/vs/editor/common/services/standaloneThemeService.ts new file mode 100644 index 00000000000..1cd8f788130 --- /dev/null +++ b/src/vs/editor/common/services/standaloneThemeService.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TokenTheme, ITokenThemeRule } from 'vs/editor/common/modes/supports/tokenization'; +import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; + +export var IStandaloneThemeService = createDecorator('themeService'); + +export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black'; +export type IColors = { [colorId: string]: string; }; + +export interface IStandaloneThemeData { + base: BuiltinTheme; + inherit: boolean; + rules: ITokenThemeRule[]; + colors: IColors; +} + +export interface IStandaloneTheme extends ITheme { + tokenTheme: TokenTheme; +} + +export interface IStandaloneThemeService extends IThemeService { + _serviceBrand: any; + + setTheme(themeName: string): string; + + defineTheme(themeName: string, themeData: IStandaloneThemeData): void; + + getTheme(): IStandaloneTheme; +} diff --git a/src/vs/editor/common/standalone/themes.ts b/src/vs/editor/common/standalone/themes.ts index 800d834c65d..311617cea47 100644 --- a/src/vs/editor/common/standalone/themes.ts +++ b/src/vs/editor/common/standalone/themes.ts @@ -5,175 +5,204 @@ 'use strict'; -import { IThemeRule } from 'vs/editor/common/modes/supports/tokenization'; +import { IStandaloneThemeData } from 'vs/editor/common/services/standaloneThemeService'; -/* -------------------------------- Begin vs tokens -------------------------------- */ -export const vs: IThemeRule[] = [ - { token: '', foreground: '000000', background: 'fffffe' }, - { token: 'invalid', foreground: 'cd3131' }, - { token: 'emphasis', fontStyle: 'italic' }, - { token: 'strong', fontStyle: 'bold' }, +/* -------------------------------- Begin vs theme -------------------------------- */ +export const vs: IStandaloneThemeData = { + base: 'vs', + inherit: false, + rules: [ + { token: '', foreground: '000000', background: 'fffffe' }, + { token: 'invalid', foreground: 'cd3131' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, - { token: 'variable', foreground: '001188' }, - { token: 'variable.predefined', foreground: '4864AA' }, - { token: 'constant', foreground: 'dd0000' }, - { token: 'comment', foreground: '008000' }, - { token: 'number', foreground: '09885A' }, - { token: 'number.hex', foreground: '3030c0' }, - { token: 'regexp', foreground: '800000' }, - { token: 'annotation', foreground: '808080' }, - { token: 'type', foreground: '008080' }, + { token: 'variable', foreground: '001188' }, + { token: 'variable.predefined', foreground: '4864AA' }, + { token: 'constant', foreground: 'dd0000' }, + { token: 'comment', foreground: '008000' }, + { token: 'number', foreground: '09885A' }, + { token: 'number.hex', foreground: '3030c0' }, + { token: 'regexp', foreground: '800000' }, + { token: 'annotation', foreground: '808080' }, + { token: 'type', foreground: '008080' }, - { token: 'delimiter', foreground: '000000' }, - { token: 'delimiter.html', foreground: '383838' }, - { token: 'delimiter.xml', foreground: '0000FF' }, + { token: 'delimiter', foreground: '000000' }, + { token: 'delimiter.html', foreground: '383838' }, + { token: 'delimiter.xml', foreground: '0000FF' }, - { token: 'tag', foreground: '800000' }, - { token: 'tag.id.jade', foreground: '4F76AC' }, - { token: 'tag.class.jade', foreground: '4F76AC' }, - { token: 'meta.scss', foreground: '800000' }, - { token: 'metatag', foreground: 'e00000' }, - { token: 'metatag.content.html', foreground: 'FF0000' }, - { token: 'metatag.html', foreground: '808080' }, - { token: 'metatag.xml', foreground: '808080' }, - { token: 'metatag.php', fontStyle: 'bold' }, + { token: 'tag', foreground: '800000' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta.scss', foreground: '800000' }, + { token: 'metatag', foreground: 'e00000' }, + { token: 'metatag.content.html', foreground: 'FF0000' }, + { token: 'metatag.html', foreground: '808080' }, + { token: 'metatag.xml', foreground: '808080' }, + { token: 'metatag.php', fontStyle: 'bold' }, - { token: 'key', foreground: '863B00' }, - { token: 'string.key.json', foreground: 'A31515' }, - { token: 'string.value.json', foreground: '0451A5' }, + { token: 'key', foreground: '863B00' }, + { token: 'string.key.json', foreground: 'A31515' }, + { token: 'string.value.json', foreground: '0451A5' }, - { token: 'attribute.name', foreground: 'FF0000' }, - { token: 'attribute.value', foreground: '0451A5' }, - { token: 'attribute.value.number', foreground: '09885A' }, - { token: 'attribute.value.unit', foreground: '09885A' }, - { token: 'attribute.value.html', foreground: '0000FF' }, - { token: 'attribute.value.xml', foreground: '0000FF' }, + { token: 'attribute.name', foreground: 'FF0000' }, + { token: 'attribute.value', foreground: '0451A5' }, + { token: 'attribute.value.number', foreground: '09885A' }, + { token: 'attribute.value.unit', foreground: '09885A' }, + { token: 'attribute.value.html', foreground: '0000FF' }, + { token: 'attribute.value.xml', foreground: '0000FF' }, - { token: 'string', foreground: 'A31515' }, - { token: 'string.html', foreground: '0000FF' }, - { token: 'string.sql', foreground: 'FF0000' }, - { token: 'string.yaml', foreground: '0451A5' }, + { token: 'string', foreground: 'A31515' }, + { token: 'string.html', foreground: '0000FF' }, + { token: 'string.sql', foreground: 'FF0000' }, + { token: 'string.yaml', foreground: '0451A5' }, - { token: 'keyword', foreground: '0000FF' }, - { token: 'keyword.json', foreground: '0451A5' }, - { token: 'keyword.flow', foreground: 'AF00DB' }, - { token: 'keyword.flow.scss', foreground: '0000FF' }, + { token: 'keyword', foreground: '0000FF' }, + { token: 'keyword.json', foreground: '0451A5' }, + { token: 'keyword.flow', foreground: 'AF00DB' }, + { token: 'keyword.flow.scss', foreground: '0000FF' }, - { token: 'operator.scss', foreground: '666666' }, - { token: 'operator.sql', foreground: '778899' }, - { token: 'operator.swift', foreground: '666666' }, - { token: 'predefined.sql', foreground: 'FF00FF' }, -]; -/* -------------------------------- End vs tokens -------------------------------- */ + { token: 'operator.scss', foreground: '666666' }, + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '666666' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + editorBackground: '#FFFFFE', + editorForeground: '#000000', + editorInactiveSelection: '#E5EBF1', + editorGuide: '#D3D3D3', + editorSelectionHighlightColor: '#ADD6FF4D' + } +}; +/* -------------------------------- End vs theme -------------------------------- */ + + +/* -------------------------------- Begin vs-dark theme -------------------------------- */ +export const vs_dark: IStandaloneThemeData = { + base: 'vs-dark', + inherit: false, + rules: [ + { token: '', foreground: 'D4D4D4', background: '1E1E1E' }, + { token: 'invalid', foreground: 'f44747' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, + + { token: 'variable', foreground: '74B0DF' }, + { token: 'variable.predefined', foreground: '4864AA' }, + { token: 'variable.parameter', foreground: '9CDCFE' }, + { token: 'constant', foreground: '569CD6' }, + { token: 'comment', foreground: '608B4E' }, + { token: 'number', foreground: 'B5CEA8' }, + { token: 'number.hex', foreground: '5BB498' }, + { token: 'regexp', foreground: 'B46695' }, + { token: 'annotation', foreground: 'cc6666' }, + { token: 'type', foreground: '3DC9B0' }, + + { token: 'delimiter', foreground: 'DCDCDC' }, + { token: 'delimiter.html', foreground: '808080' }, + { token: 'delimiter.xml', foreground: '808080' }, + + { token: 'tag', foreground: '569CD6' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta.scss', foreground: 'A79873' }, + { token: 'meta.tag', foreground: 'CE9178' }, + { token: 'metatag', foreground: 'DD6A6F' }, + { token: 'metatag.content.html', foreground: '9CDCFE' }, + { token: 'metatag.html', foreground: '569CD6' }, + { token: 'metatag.xml', foreground: '569CD6' }, + { token: 'metatag.php', fontStyle: 'bold' }, + + { token: 'key', foreground: '9CDCFE' }, + { token: 'string.key.json', foreground: '9CDCFE' }, + { token: 'string.value.json', foreground: 'CE9178' }, + + { token: 'attribute.name', foreground: '9CDCFE' }, + { token: 'attribute.value', foreground: 'CE9178' }, + { token: 'attribute.value.number.css', foreground: 'B5CEA8' }, + { token: 'attribute.value.unit.css', foreground: 'B5CEA8' }, + { token: 'attribute.value.hex.css', foreground: 'D4D4D4' }, + + { token: 'string', foreground: 'CE9178' }, + { token: 'string.sql', foreground: 'FF0000' }, + + { token: 'keyword', foreground: '569CD6' }, + { token: 'keyword.flow', foreground: 'C586C0' }, + { token: 'keyword.json', foreground: 'CE9178' }, + { token: 'keyword.flow.scss', foreground: '569CD6' }, + + { token: 'operator.scss', foreground: '909090' }, + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '909090' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + editorBackground: '#1E1E1E', + editorForeground: '#D4D4D4', + editorInactiveSelection: '#3A3D41', + editorGuide: '#404040', + editorSelectionHighlightColor: '#ADD6FF26' + } +}; +/* -------------------------------- End vs-dark theme -------------------------------- */ -/* -------------------------------- Begin vs-dark tokens -------------------------------- */ -export const vs_dark: IThemeRule[] = [ - { token: '', foreground: 'D4D4D4', background: '1E1E1E' }, - { token: 'invalid', foreground: 'f44747' }, - { token: 'emphasis', fontStyle: 'italic' }, - { token: 'strong', fontStyle: 'bold' }, +/* -------------------------------- Begin hc-black theme -------------------------------- */ +export const hc_black: IStandaloneThemeData = { + base: 'hc-black', + inherit: false, + rules: [ + { token: '', foreground: 'FFFFFF', background: '000000' }, + { token: 'invalid', foreground: 'f44747' }, + { token: 'emphasis', fontStyle: 'italic' }, + { token: 'strong', fontStyle: 'bold' }, - { token: 'variable', foreground: '74B0DF' }, - { token: 'variable.predefined', foreground: '4864AA' }, - { token: 'variable.parameter', foreground: '9CDCFE' }, - { token: 'constant', foreground: '569CD6' }, - { token: 'comment', foreground: '608B4E' }, - { token: 'number', foreground: 'B5CEA8' }, - { token: 'number.hex', foreground: '5BB498' }, - { token: 'regexp', foreground: 'B46695' }, - { token: 'annotation', foreground: 'cc6666' }, - { token: 'type', foreground: '3DC9B0' }, + { token: 'variable', foreground: '1AEBFF' }, + { token: 'variable.parameter', foreground: '9CDCFE' }, + { token: 'constant', foreground: '569CD6' }, + { token: 'comment', foreground: '608B4E' }, + { token: 'number', foreground: 'FFFFFF' }, + { token: 'regexp', foreground: 'C0C0C0' }, + { token: 'annotation', foreground: '569CD6' }, + { token: 'type', foreground: '3DC9B0' }, - { token: 'delimiter', foreground: 'DCDCDC' }, - { token: 'delimiter.html', foreground: '808080' }, - { token: 'delimiter.xml', foreground: '808080' }, + { token: 'delimiter', foreground: 'FFFF00' }, + { token: 'delimiter.html', foreground: 'FFFF00' }, - { token: 'tag', foreground: '569CD6' }, - { token: 'tag.id.jade', foreground: '4F76AC' }, - { token: 'tag.class.jade', foreground: '4F76AC' }, - { token: 'meta.scss', foreground: 'A79873' }, - { token: 'meta.tag', foreground: 'CE9178' }, - { token: 'metatag', foreground: 'DD6A6F' }, - { token: 'metatag.content.html', foreground: '9CDCFE' }, - { token: 'metatag.html', foreground: '569CD6' }, - { token: 'metatag.xml', foreground: '569CD6' }, - { token: 'metatag.php', fontStyle: 'bold' }, + { token: 'tag', foreground: '569CD6' }, + { token: 'tag.id.jade', foreground: '4F76AC' }, + { token: 'tag.class.jade', foreground: '4F76AC' }, + { token: 'meta', foreground: 'D4D4D4' }, + { token: 'meta.tag', foreground: 'CE9178' }, + { token: 'metatag', foreground: '569CD6' }, + { token: 'metatag.content.html', foreground: '1AEBFF' }, + { token: 'metatag.html', foreground: '569CD6' }, + { token: 'metatag.xml', foreground: '569CD6' }, + { token: 'metatag.php', fontStyle: 'bold' }, - { token: 'key', foreground: '9CDCFE' }, - { token: 'string.key.json', foreground: '9CDCFE' }, - { token: 'string.value.json', foreground: 'CE9178' }, + { token: 'key', foreground: '9CDCFE' }, + { token: 'string.key', foreground: '9CDCFE' }, + { token: 'string.value', foreground: 'CE9178' }, - { token: 'attribute.name', foreground: '9CDCFE' }, - { token: 'attribute.value', foreground: 'CE9178' }, - { token: 'attribute.value.number.css', foreground: 'B5CEA8' }, - { token: 'attribute.value.unit.css', foreground: 'B5CEA8' }, - { token: 'attribute.value.hex.css', foreground: 'D4D4D4' }, + { token: 'attribute.name', foreground: '569CD6' }, + { token: 'attribute.value', foreground: '3FF23F' }, - { token: 'string', foreground: 'CE9178' }, - { token: 'string.sql', foreground: 'FF0000' }, + { token: 'string', foreground: 'CE9178' }, + { token: 'string.sql', foreground: 'FF0000' }, - { token: 'keyword', foreground: '569CD6' }, - { token: 'keyword.flow', foreground: 'C586C0' }, - { token: 'keyword.json', foreground: 'CE9178' }, - { token: 'keyword.flow.scss', foreground: '569CD6' }, + { token: 'keyword', foreground: '569CD6' }, + { token: 'keyword.flow', foreground: 'C586C0' }, - { token: 'operator.scss', foreground: '909090' }, - { token: 'operator.sql', foreground: '778899' }, - { token: 'operator.swift', foreground: '909090' }, - { token: 'predefined.sql', foreground: 'FF00FF' }, -]; -/* -------------------------------- End vs-dark tokens -------------------------------- */ - - - -/* -------------------------------- Begin hc-black tokens -------------------------------- */ -export const hc_black: IThemeRule[] = [ - { token: '', foreground: 'FFFFFF', background: '000000' }, - { token: 'invalid', foreground: 'f44747' }, - { token: 'emphasis', fontStyle: 'italic' }, - { token: 'strong', fontStyle: 'bold' }, - - { token: 'variable', foreground: '1AEBFF' }, - { token: 'variable.parameter', foreground: '9CDCFE' }, - { token: 'constant', foreground: '569CD6' }, - { token: 'comment', foreground: '608B4E' }, - { token: 'number', foreground: 'FFFFFF' }, - { token: 'regexp', foreground: 'C0C0C0' }, - { token: 'annotation', foreground: '569CD6' }, - { token: 'type', foreground: '3DC9B0' }, - - { token: 'delimiter', foreground: 'FFFF00' }, - { token: 'delimiter.html', foreground: 'FFFF00' }, - - { token: 'tag', foreground: '569CD6' }, - { token: 'tag.id.jade', foreground: '4F76AC' }, - { token: 'tag.class.jade', foreground: '4F76AC' }, - { token: 'meta', foreground: 'D4D4D4' }, - { token: 'meta.tag', foreground: 'CE9178' }, - { token: 'metatag', foreground: '569CD6' }, - { token: 'metatag.content.html', foreground: '1AEBFF' }, - { token: 'metatag.html', foreground: '569CD6' }, - { token: 'metatag.xml', foreground: '569CD6' }, - { token: 'metatag.php', fontStyle: 'bold' }, - - { token: 'key', foreground: '9CDCFE' }, - { token: 'string.key', foreground: '9CDCFE' }, - { token: 'string.value', foreground: 'CE9178' }, - - { token: 'attribute.name', foreground: '569CD6' }, - { token: 'attribute.value', foreground: '3FF23F' }, - - { token: 'string', foreground: 'CE9178' }, - { token: 'string.sql', foreground: 'FF0000' }, - - { token: 'keyword', foreground: '569CD6' }, - { token: 'keyword.flow', foreground: 'C586C0' }, - - { token: 'operator.sql', foreground: '778899' }, - { token: 'operator.swift', foreground: '909090' }, - { token: 'predefined.sql', foreground: 'FF00FF' }, -]; - -/* -------------------------------- End hc-black tokens -------------------------------- */ + { token: 'operator.sql', foreground: '778899' }, + { token: 'operator.swift', foreground: '909090' }, + { token: 'predefined.sql', foreground: 'FF00FF' }, + ], + colors: { + editorBackground: '#000000', + editorForeground: '#FFFFFF', + editorGuide: '#FFFFFF', + } +}; +/* -------------------------------- End hc-black theme -------------------------------- */ diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 723dca1bc61..46b0a81201b 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -3,25 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import nls = require('vs/nls'); +import { registerColor, editorBackground, highContrastOutline } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; + /** * Definition of the editor colors */ - -import nls = require('vs/nls'); -import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; -import { Registry } from 'vs/platform/platform'; -import { ITheme, ICssStyleCollector, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; -import { Color } from 'vs/base/common/color'; - - -let colorReg = Registry.as(colorRegistry.Extensions.ColorContribution); -let themingReg = Registry.as(ThemingExtensions.ThemingContribution); -themingReg.onThemeChange(applyEditorStyles); - -function registerColor(id: string, defaults: colorRegistry.ColorDefaults, description: string): colorRegistry.ColorIdentifier { - return colorReg.registerColor(id, defaults, description); -} - export const editorLineHighlight = registerColor('editorLineHighlight', { dark: null, light: null, hc: null }, nls.localize('lineHighlight', 'Editor line highlight color')); export const editorLineHighlightBorder = registerColor('editorLineHighlightBorderCox', { dark: '#282828', light: '#eeeeee', hc: '#f38518' }, nls.localize('lineHighlightBorderBox', 'Editor line highlight border box color')); export const editorRangeHighlight = registerColor('editorRangeHighlight', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of range highlighted, like by Quick open and Find features')); @@ -29,34 +18,27 @@ export const editorCursor = registerColor('editorCursor', { dark: '#AEAFAD', lig export const editorInvisibles = registerColor('editorInvisibles', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('invisibles', 'Editor invisibles color')); export const editorGuide = registerColor('editorGuide', { dark: editorInvisibles, light: editorInvisibles, hc: editorInvisibles }, nls.localize('guide', 'Editor guide color')); -// TBD: split up and place each rule in the owning part -function applyEditorStyles(theme: ITheme, collector: ICssStyleCollector) { +// contains all color rules that used to defined in editor/browser/widget/editor.css +registerThemingParticipant((theme, collector) => { - let background = theme.getColor(colorRegistry.editorBackground); + let background = theme.getColor(editorBackground); if (background) { - addBackgroundColorRule(theme, '.monaco-editor-background', background, collector); - collector.addRule(`.${theme.selector} .monaco-workbench .monaco-editor-background { background-color: ${background}; }`); + collector.addRule(`.monaco-editor.${theme.selector} .monaco-editor-background { background-color: ${background}; }`); } - let lineHighlight = theme.getColor(editorLineHighlight); - if (lineHighlight) { - collector.addRule(`.monaco-editor.${theme.selector} .view-overlays .current-line { background-color: ${lineHighlight}; border: none; }`); - collector.addRule(`.monaco-editor.${theme.selector} .margin-view-overlays .current-line-margin { background-color: ${lineHighlight}; border: none; }`); - } else { - // to do editor line border + let rangeHighlight = theme.getColor(editorRangeHighlight); + if (rangeHighlight) { + collector.addRule(`.monaco-editor.${theme.selector} .rangeHighlight { background-color: ${rangeHighlight}; }`); + } + let outline = theme.getColor(highContrastOutline); + if (outline) { + collector.addRule(`.monaco-editor.${theme.selector} .rangeHighlight { border: 1px dotted ${outline}; }; }`); } - addBackgroundColorRule(theme, '.rangeHighlight', theme.getColor(editorRangeHighlight), collector); let invisibles = theme.getColor(editorInvisibles); if (invisibles) { collector.addRule(`.vs-whitespace { color: ${invisibles} !important; }`); } -} - -function addBackgroundColorRule(theme: ITheme, selector: string, color: Color, collector: ICssStyleCollector): void { - if (color) { - collector.addRule(`.monaco-editor.${theme.selector} ${selector} { background-color: ${color}; }`); - } -} +}); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.css b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.css index 058ef445e0f..4b699e60424 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.css +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.css @@ -26,11 +26,6 @@ .monaco-editor .goto-definition-link { text-decoration: underline; cursor: pointer; - color: blue !important; -} - -.monaco-editor.vs-dark .goto-definition-link { - color: #4E94CE !important; } .monaco-editor .goto-definition-link-hover { diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts index 8d157ccb2e8..0a0b6f820b0 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -136,7 +136,7 @@ export class DefinitionAction extends EditorAction { return editorService.openEditor({ resource: uri, options: { - selection: range, + selection: Range.collapseToStart(range), revealIfVisible: !sideBySide } }, sideBySide).then(editor => { @@ -634,4 +634,4 @@ registerThemingParticipant((theme, collector) => { if (activeLinkForeground) { collector.addRule(`.monaco-editor.${theme.selector} .goto-definition-link { color: ${activeLinkForeground} !important; }`); } -}); \ No newline at end of file +}); diff --git a/src/vs/editor/contrib/hover/browser/hover.css b/src/vs/editor/contrib/hover/browser/hover.css index 2566de60e0d..a556acc7b01 100644 --- a/src/vs/editor/contrib/hover/browser/hover.css +++ b/src/vs/editor/contrib/hover/browser/hover.css @@ -7,8 +7,6 @@ cursor: default; position: absolute; overflow: hidden; - background-color: #F3F3F3; - border: 1px solid #CCC; z-index: 50; -webkit-user-select: text; -ms-user-select: text; @@ -33,10 +31,6 @@ padding: 4px 5px; } -.monaco-editor-hover .hover-row:not(:first-child):not(:empty) { - border-top: 1px solid rgba(204, 204, 204, 0.5); -} - .monaco-editor-hover p, .monaco-editor-hover ul { margin: 8px 0; @@ -68,14 +62,5 @@ word-break: break-all; } -.monaco-editor .hoverHighlight { - background: rgba(173, 214, 255, 0.15); -} - -.monaco-editor.vs-dark .monaco-editor-hover { background-color: #2D2D30; border-color: #555; } .monaco-editor.vs-dark .monaco-editor-hover a { color: #1C5DAF; } -.monaco-editor.vs-dark .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-color: rgba(85,85,85,0.5); } -.monaco-editor.vs-dark .hoverHighlight { background: rgba(38, 79, 120, 0.25); } - -.monaco-editor.hc-black .monaco-editor-hover { background-color: #0C141F; } .monaco-editor.hc-black .monaco-editor-hover a { color: #1C5DAF; } \ No newline at end of file diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts index 4195e2fe4c2..d4d697c4f55 100644 --- a/src/vs/editor/contrib/hover/browser/hover.ts +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -26,7 +26,10 @@ import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import EditorContextKeys = editorCommon.EditorContextKeys; -export const editorHoverHighlight = registerColor('editorHoverHighlight', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Background color of the editor hover')); +export const editorHoverHighlight = registerColor('editorHoverHighlight', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown.')); +export const editorHoverBackground = registerColor('editorHoverBackground', { light: '#F3F3F3', dark: '#2D2D30', hc: '#0C141F' }, nls.localize('hoverBackground', 'Background color of the editor hover.')); +export const editorHoverBorder = registerColor('editorHoverBorder', { light: '#CCCCCC', dark: '#555555', hc: '#CCCCCC' }, nls.localize('hoverBorder', 'Border color of the editor hover.')); + @editorContribution export class ModesHoverController implements editorCommon.IEditorContribution { @@ -181,4 +184,13 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (editorHoverHighlightColor) { collector.addRule(`.monaco-editor.${theme.selector} .hoverHighlight { background-color: ${editorHoverHighlightColor}; }`); } + let hoverBackground = theme.getColor(editorHoverBackground); + if (hoverBackground) { + collector.addRule(`.monaco-editor.${theme.selector} .monaco-editor-hover { background-color: ${hoverBackground}; }`); + } + let hoverBorder = theme.getColor(editorHoverBorder); + if (hoverBorder) { + collector.addRule(`.monaco-editor.${theme.selector} .monaco-editor-hover { border: 1px solid ${hoverBorder}; }`); + collector.addRule(`.monaco-editor.${theme.selector} .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`); + } }); diff --git a/src/vs/editor/contrib/inspectTokens/browser/inspectTokens.ts b/src/vs/editor/contrib/inspectTokens/browser/inspectTokens.ts index 7a49c678b79..66647f525cd 100644 --- a/src/vs/editor/contrib/inspectTokens/browser/inspectTokens.ts +++ b/src/vs/editor/contrib/inspectTokens/browser/inspectTokens.ts @@ -17,7 +17,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding'; import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType, ITokenizationSupport, IState } from 'vs/editor/common/modes'; import { CharCode } from 'vs/base/common/charCode'; -import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService'; +import { IStandaloneThemeService } from 'vs/editor/common/services/standaloneThemeService'; import { NULL_STATE, nullTokenize, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; import { Token } from 'vs/editor/common/core/token'; import { Color } from 'vs/base/common/color'; @@ -32,18 +32,18 @@ class InspectTokensController extends Disposable implements IEditorContribution } private _editor: ICodeEditor; - private _standaloneColorService: IStandaloneColorService; + private _standaloneThemeService: IStandaloneThemeService; private _modeService: IModeService; private _widget: InspectTokensWidget; constructor( editor: ICodeEditor, - @IStandaloneColorService standaloneColorService: IStandaloneColorService, + @IStandaloneThemeService standaloneColorService: IStandaloneThemeService, @IModeService modeService: IModeService ) { super(); this._editor = editor; - this._standaloneColorService = standaloneColorService; + this._standaloneThemeService = standaloneColorService; this._modeService = modeService; this._widget = null; @@ -68,7 +68,7 @@ class InspectTokensController extends Disposable implements IEditorContribution if (!this._editor.getModel()) { return; } - this._widget = new InspectTokensWidget(this._editor, this._standaloneColorService, this._modeService); + this._widget = new InspectTokensWidget(this._editor, this._standaloneThemeService, this._modeService); } public stop(): void { @@ -166,7 +166,7 @@ class InspectTokensWidget extends Disposable implements IContentWidget { public allowEditorOverflow = true; private _editor: ICodeEditor; - private _standaloneColorService: IStandaloneColorService; + private _standaloneThemeService: IStandaloneThemeService; private _modeService: IModeService; private _tokenizationSupport: ITokenizationSupport; private _model: IModel; @@ -174,12 +174,12 @@ class InspectTokensWidget extends Disposable implements IContentWidget { constructor( editor: ICodeEditor, - standaloneColorService: IStandaloneColorService, + standaloneThemeService: IStandaloneThemeService, modeService: IModeService ) { super(); this._editor = editor; - this._standaloneColorService = standaloneColorService; + this._standaloneThemeService = standaloneThemeService; this._modeService = modeService; this._model = this._editor.getModel(); this._domNode = document.createElement('div'); diff --git a/src/vs/editor/contrib/links/browser/links.css b/src/vs/editor/contrib/links/browser/links.css index aaf5cd16734..9e0a5218571 100644 --- a/src/vs/editor/contrib/links/browser/links.css +++ b/src/vs/editor/contrib/links/browser/links.css @@ -9,13 +9,4 @@ .monaco-editor .detected-link-active { cursor: pointer; - color: blue !important; -} - -.monaco-editor.vs-dark .detected-link-active { - color: #4E94CE !important; -} - -.monaco-editor.hc-black .detected-link-active { - color: cyan !important; } diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css index 0b5d2ea1f12..53396457103 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.css @@ -37,30 +37,11 @@ padding: 3em 0; } -.monaco-editor .reference-zone-widget, -.monaco-editor .reference-zone-widget .preview .monaco-editor, -.monaco-editor .reference-zone-widget .preview .glyph-margin, -.monaco-editor .reference-zone-widget .preview .monaco-editor-background, -.monaco-editor .reference-zone-widget .preview .monaco-editor .margin .view-line { - background-color: #F2F8FC; -} - -.monaco-editor .reference-zone-widget .preview .reference-decoration { - background-color: rgba(245, 216, 2, 0.87); -} - .monaco-editor .reference-zone-widget .ref-tree { - background-color: #F3F3F3; - color: #646465; line-height: 22px; font-size: 13px; } -.monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { - background-color: rgba(51, 153, 255, .2); - color: #6C6C6C !important; -} - .monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected.has-children > .content:before { border-left-color: #A6A6A6; } @@ -82,7 +63,6 @@ .monaco-editor .reference-zone-widget .ref-tree .reference-file { position: relative; line-height: 22px; - color: #1E1E1E; } .monaco-editor .reference-zone-widget .ref-tree .reference-file .directory { @@ -99,10 +79,6 @@ color: white; } -.monaco-editor .reference-zone-widget .ref-tree .referenceMatch { - background-color: rgba(234, 92, 0, 0.3); -} - .monaco-editor .reference-zone-widget .monaco-count-badge { margin-right: 12px; } @@ -114,27 +90,6 @@ background-color: #1e1e1e; } -.monaco-editor.vs-dark .reference-zone-widget, -.monaco-editor.vs-dark .reference-zone-widget .preview .monaco-editor, -.monaco-editor.vs-dark .reference-zone-widget .preview .glyph-margin, -.monaco-editor.vs-dark .reference-zone-widget .preview .monaco-editor-background, -.monaco-editor.vs-dark .reference-zone-widget .preview .monaco-editor .margin .view-line { - background-color: #001F33; -} - -.monaco-editor.vs-dark .reference-zone-widget .ref-tree { - background-color: #252526; - color: #BBB; -} - -.monaco-editor.vs-dark .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { - color: #FFF !important; -} - -.monaco-editor.vs-dark .reference-zone-widget .preview .reference-decoration { - background-color: rgba(255, 143, 0, 0.6); -} - .monaco-editor.vs-dark .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected.has-children > .content:before { border-left-color: white; } @@ -148,45 +103,13 @@ border-left-color: transparent; } -.monaco-editor.vs-dark .reference-zone-widget .ref-tree .reference-file { - color: #FFF; -} - /* High Contrast Theming */ -.monaco-editor.hc-black .reference-zone-widget, -.monaco-editor.hc-black .reference-zone-widget .preview .monaco-editor, -.monaco-editor.hc-black .reference-zone-widget .preview .monaco-editor-background, -.monaco-editor.hc-black .reference-zone-widget .preview .monaco-editor .margin .view-line { - background-color: #0C141F; -} - -.monaco-editor.hc-black .reference-zone-widget .preview .reference-decoration { - background: none; - border: 2px solid #f38518; - box-sizing: border-box; -} - -.monaco-editor.hc-black .reference-zone-widget .ref-tree { - background-color: #000; - color: #fff; -} .monaco-editor.hc-black .reference-zone-widget .ref-tree .reference-file { - color: #fff; line-height: 20px; font-weight: bold; } .monaco-editor.hc-black .reference-zone-widget .ref-tree .reference-file .directory { font-weight: normal; -} - -.monaco-editor.hc-black .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { - background: none; - color: #FFF !important; -} - -.monaco-editor.hc-black .reference-zone-widget .ref-tree .referenceMatch { - background: none; - border: 1px dotted #f38518; } \ No newline at end of file diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts index a1be31da088..c34b6013868 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts @@ -37,11 +37,9 @@ import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeE import { PeekViewWidget, IPeekViewService } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; import { FileReferences, OneReference, ReferencesModel } from './referencesModel'; import { ITextModelResolverService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, highContrastOutline } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -export const referencesFindMatchHighlight = registerColor('referencesFindMatchHighlight', { dark: '#ea5c004d', light: '#ea5c004d', hc: null }, nls.localize('referencesFindMatchHighlight', 'References view match highlight color')); -export const referencesReferenceHighlight = registerColor('referencesReferenceHighlight', { dark: '#ff8f0099', light: '#f5d802de', hc: null }, nls.localize('referencesReferenceHighlight', 'References range highlight color')); class DecorationsManager implements IDisposable { @@ -144,7 +142,7 @@ class DecorationsManager implements IDisposable { this._editor.changeDecorations((accessor) => { for (let i = 0, len = toRemove.length; i < len; i++) { - delete this._decorations[toRemove[i]]; + this._decorations.delete(toRemove[i]); } accessor.deltaDecorations(toRemove, []); }); @@ -758,13 +756,62 @@ export class ReferenceWidget extends PeekViewWidget { } } +// theming + +export const editorPeekFindMatchHighlight = registerColor('editorPeekFindMatchHighlight', { dark: '#ea5c004d', light: '#ea5c004d', hc: null }, nls.localize('editorPeekFindMatchHighlight', 'References view match highlight color')); +export const editorPeekReferenceHighlight = registerColor('editorPeekReferenceHighlight', { dark: '#ff8f0099', light: '#f5d802de', hc: null }, nls.localize('editorPeekReferenceHighlight', 'References range highlight color')); + +export const editorPeekResultsBackground = registerColor('editorPeekResultsBackground', { dark: '#252526', light: '#F3F3F3', hc: '#000000' }, nls.localize('editorPeekResultsBackground', 'References view list background')); +export const editorPeekResultsMatchForeground = registerColor('editorPeekResultsMatchForeground', { dark: '#bbbbbb', light: '#646465', hc: '#ffffff' }, nls.localize('editorPeekResultsMatchForeground', 'References view match entry foreground')); +export const editorPeekResultsFileForeground = registerColor('editorPeekResultsFileForeground', { dark: '#ffffff', light: '#f5d802de', hc: '#ffffff' }, nls.localize('editorPeekResultsFileForeground', 'References view file entry foreground')); +export const editorPeekResultsSelectedBackground = registerColor('editorPeekResultsSelectedBackground', { dark: '#3399ff33', light: '#3399ff33', hc: null }, nls.localize('editorPeekResultsSelectedBackground', 'References view selected entry background')); +export const editorPeekResultsSelectedForeground = registerColor('editorPeekResultsSelectedForeground', { dark: '#ffffff', light: '#6C6C6C', hc: '#ffffff' }, nls.localize('editorPeekResultsSelectedForeground', 'References view selected entry foreground')); +export const editorPeekEditorBackground = registerColor('editorPeekEditorBackground', { dark: '#001F33', light: '#F2F8FC', hc: '#0C141F' }, nls.localize('editorPeekEditorBackground', 'References view editor background')); + + registerThemingParticipant((theme, collector) => { - let referencesFindMatchHighlightColor = theme.getColor(referencesFindMatchHighlight); - if (referencesFindMatchHighlightColor) { - collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .referenceMatch { background-color: ${referencesFindMatchHighlightColor}; }`); + let findMatchHighlightColor = theme.getColor(editorPeekFindMatchHighlight); + if (findMatchHighlightColor) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .referenceMatch { background-color: ${findMatchHighlightColor}; }`); } - let referencesReferenceHighlightColor = theme.getColor(referencesReferenceHighlight); - if (referencesReferenceHighlightColor) { - collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .preview .reference-decoration { background-color: ${referencesReferenceHighlightColor}; }`); + let referenceHighlightColor = theme.getColor(editorPeekReferenceHighlight); + if (referenceHighlightColor) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .preview .reference-decoration { background-color: ${referenceHighlightColor}; }`); + } + let hcOutline = theme.getColor(highContrastOutline); + if (hcOutline) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .referenceMatch { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .preview .reference-decoration { border: 2px solid ${hcOutline}; box-sizing: border-box; }`); + } + let resultsBackground = theme.getColor(editorPeekResultsBackground); + if (resultsBackground) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree { background-color: ${resultsBackground}; }`); + } + let resultsMatchForeground = theme.getColor(editorPeekResultsMatchForeground); + if (resultsMatchForeground) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree { color: ${resultsMatchForeground}; }`); + } + let resultsFileForeground = theme.getColor(editorPeekResultsFileForeground); + if (resultsFileForeground) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .reference-file { color: ${resultsFileForeground}; }`); + } + let resultsSelectedBackground = theme.getColor(editorPeekResultsSelectedBackground); + if (resultsSelectedBackground) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${resultsSelectedBackground}; }`); + } + let resultsSelectedForeground = theme.getColor(editorPeekResultsSelectedForeground); + if (resultsSelectedForeground) { + collector.addRule(`.monaco-editor.${theme.selector} .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${resultsSelectedForeground} !important; }`); + } + let editorBackground = theme.getColor(editorPeekEditorBackground); + if (editorBackground) { + collector.addRule( + `.monaco-editor.${theme.selector} .reference-zone-widget,` + + `.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor,` + + `.monaco-editor.${theme.selector} .reference-zone-widget .preview .glyph-margin,` + + `.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor-background,` + + `.monaco-editor.${theme.selector} .reference-zone-widget .preview .monaco-editor .margin .view-line {` + + ` background-color: ${editorBackground};` + + `}`); } }); \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/Class_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Class_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Class_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Class_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Class_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Class_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Class_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/ColorPalette_16x.svg b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/ColorPalette_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/ColorPalette_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/ColorPalette_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/ColorPalette_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/ColorPalette_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Document_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Document_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Document_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Document_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Document_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Document_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Document_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/EnumItem_16x.svg b/src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/EnumItem_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/EnumItem_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/EnumItem_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/EnumItem_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/EnumItem_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Enumerator_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Enumerator_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Enumerator_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Enumerator_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Enumerator_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Enumerator_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Field_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Field_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Field_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Field_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Field_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Field_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Field_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Folder_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Folder_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Folder_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Folder_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Folder_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Folder_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg new file mode 100644 index 00000000000..5511fc9e239 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg new file mode 100644 index 00000000000..604d994cbd5 --- /dev/null +++ b/src/vs/editor/contrib/suggest/browser/media/ImportFile_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/browser/IntelliSenseKeyword_16x.svg b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/IntelliSenseKeyword_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/IntelliSenseKeyword_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/IntelliSenseKeyword_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/IntelliSenseKeyword_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Interface_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Interface_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Interface_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Interface_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Interface_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Interface_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Method_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Method_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Method_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Method_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Method_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Method_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Method_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Misc_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Misc_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Misc_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Misc_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Misc_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Misc_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Namespace_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Namespace_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Namespace_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Namespace_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Namespace_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Namespace_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Property_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Property_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Property_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Property_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Property_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Property_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Property_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Ruler_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Ruler_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Ruler_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Ruler_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Ruler_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Ruler_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Snippet_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Snippet_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Snippet_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/Snippet_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/Snippet_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/Snippet_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/String_16x.svg b/src/vs/editor/contrib/suggest/browser/media/String_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/String_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/String_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/String_inverse_16x.svg b/src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/String_inverse_16x.svg rename to src/vs/editor/contrib/suggest/browser/media/String_inverse_16x.svg diff --git a/src/vs/editor/contrib/suggest/browser/back.svg b/src/vs/editor/contrib/suggest/browser/media/back.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/back.svg rename to src/vs/editor/contrib/suggest/browser/media/back.svg diff --git a/src/vs/editor/contrib/suggest/browser/info.svg b/src/vs/editor/contrib/suggest/browser/media/info.svg similarity index 100% rename from src/vs/editor/contrib/suggest/browser/info.svg rename to src/vs/editor/contrib/suggest/browser/media/info.svg diff --git a/src/vs/editor/contrib/suggest/browser/suggest.css b/src/vs/editor/contrib/suggest/browser/media/suggest.css similarity index 95% rename from src/vs/editor/contrib/suggest/browser/suggest.css rename to src/vs/editor/contrib/suggest/browser/media/suggest.css index 657a0b01b96..57744fdc56a 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.css +++ b/src/vs/editor/contrib/suggest/browser/media/suggest.css @@ -144,12 +144,13 @@ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.property { background-image: url('Property_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.unit { background-image: url('Ruler_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.value, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('EnumItem_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.text { background-image: url('String_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.color { background-image: url('ColorPalette_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('Enumerator_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_16x.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor { background-image: none; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.folder { background-image: url('Folder_16x.svg'); } @@ -294,7 +295,9 @@ .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.value, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.value, .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('EnumItem_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum { background-image: url('Enumerator_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.enum-member, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.enum-member { background-image: url('EnumItem_inverse_16x.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.keyword, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.keyword { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } @@ -309,7 +312,7 @@ .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.file { background-image: url('Document_inverse_16x.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.reference, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('Enumerator_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.reference { background-image: url('ImportFile_16x_vscode_inverse.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon.snippet, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon.snippet { background-image: url('Snippet_inverse_16x.svg'); } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index cc1923a2350..9f7663edc5d 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -5,7 +5,7 @@ 'use strict'; -import 'vs/css!./suggest'; +import 'vs/css!./media/suggest'; import * as nls from 'vs/nls'; import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.css b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.css deleted file mode 100644 index 513295fd83e..00000000000 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.css +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-editor.vs .selectionHighlight { background: rgba(173, 214, 255, 0.3); } -.monaco-editor.vs-dark .selectionHighlight { background: rgba(173, 214, 255, 0.15); } -.monaco-editor.hc-black .selectionHighlight { border: 1px dotted #f38518; box-sizing: border-box; } - -.monaco-editor.vs .wordHighlight { background: rgba(87, 87, 87, 0.25); } -.monaco-editor.vs-dark .wordHighlight { background: rgba(87, 87, 87, 0.72); } -.monaco-editor.hc-black .wordHighlight { background: none; border: 1px dashed #f38518; box-sizing: border-box; } - -.monaco-editor.vs .wordHighlightStrong { background: rgba(14, 99, 156, 0.25); } -.monaco-editor.vs-dark .wordHighlightStrong { background: rgba(0, 73, 114, 0.72); } -.monaco-editor.hc-black .wordHighlightStrong { background: none; border: 1px dashed #f38518; box-sizing: border-box;} diff --git a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts index dbde4643a7b..b1ac64479f7 100644 --- a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts @@ -16,13 +16,11 @@ import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProviderRegi import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; -import { registerColor, editorSelectionHighlightColor } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { Color } from 'vs/base/common/color'; +import { registerColor, editorSelectionHighlightColor, highContrastOutline } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export const editorWordHighlight = registerColor('editorWordHighlight', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable')); -export const editorWordHighlightString = registerColor('editorWordHighlightStrong', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable')); - +export const editorWordHighlightStrong = registerColor('editorWordHighlightStrong', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable')); export function getOccurrencesAtPosition(model: editorCommon.IReadOnlyModel, position: Position): TPromise { @@ -318,20 +316,25 @@ class WordHighlighterContribution implements editorCommon.IEditorContribution { } } -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { - let selectionHighlightColor = theme.getColor(editorSelectionHighlightColor); - if (selectionHighlightColor) { - addBackgroundColorRule(theme, '.focused .selectionHighlight', selectionHighlightColor, collector); - addBackgroundColorRule(theme, '.selectionHighlight', selectionHighlightColor.transparent(0.5), collector); +registerThemingParticipant((theme, collector) => { + let selectionHighlight = theme.getColor(editorSelectionHighlightColor); + if (selectionHighlight) { + collector.addRule(`.monaco-editor.${theme.selector} .focused .selectionHighlight { background-color: ${selectionHighlight}; }`); + collector.addRule(`.monaco-editor.${theme.selector} .selectionHighlight { background-color: ${selectionHighlight.transparent(0.5)}; }`); + } + let wordHighlight = theme.getColor(editorWordHighlight); + if (wordHighlight) { + collector.addRule(`.monaco-editor.${theme.selector} .wordHighlight { background-color: ${wordHighlight}; }`); + } + let wordHighlightStrong = theme.getColor(editorWordHighlightStrong); + if (wordHighlightStrong) { + collector.addRule(`.monaco-editor.${theme.selector} .wordHighlightStrong { background-color: ${wordHighlightStrong}; }`); + } + let hcOutline = theme.getColor(highContrastOutline); + if (hcOutline) { + collector.addRule(`.monaco-editor.${theme.selector} .selectionHighlight { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor.${theme.selector} .wordHighlight { border: 1px dashed ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor.${theme.selector} .wordHighlightStrong { border: 1px dashed ${hcOutline}; box-sizing: border-box; }`); } - addBackgroundColorRule(theme, '.wordHighlight', theme.getColor(editorWordHighlight), collector); - addBackgroundColorRule(theme, '.wordHighlightStrong', theme.getColor(editorWordHighlightString), collector); - -}); - -function addBackgroundColorRule(theme: ITheme, selector: string, color: Color, collector: ICssStyleCollector): void { - if (color) { - collector.addRule(`.monaco-editor.${theme.selector} ${selector} { background-color: ${color}; }`); - } -} \ No newline at end of file +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css index 7dd65c91beb..66762c55d10 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css +++ b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.css @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .peekview-widget .head { - background-color: #fff; -webkit-box-sizing: border-box; -o-box-sizing: border-box; -moz-box-sizing: border-box; @@ -19,13 +18,8 @@ cursor: pointer; } -.monaco-editor .peekview-widget .head .peekview-title .filename { - color: #333333; -} - .monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) { font-size: 0.9em; - color: rgba(108, 108, 108, 0.7); margin-left: 0.5em; } @@ -49,42 +43,13 @@ .monaco-editor .peekview-widget > .body { border-top: 1px solid; position: relative; - border-color: rgb(0, 122, 204); } /* Dark Theme */ -.monaco-editor.vs-dark .peekview-widget .head { - background-color: #1E1E1E; -} +/* High Contrast Theme */ .monaco-editor.hc-black .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action, .monaco-editor.vs-dark .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action { background: url('media/close-inverse.svg') center center no-repeat; } -.monaco-editor.vs-dark .peekview-widget .head .peekview-title .filename { - color: white; -} - -.monaco-editor.vs-dark .peekview-widget .head .peekview-title .dirname { - color: rgba(204, 204, 204, 0.7); -} - -/* High Contrast Theming */ -.monaco-editor.hc-black .peekview-widget .head { - background-color: #0C141F; - border-color: #6FC3DF; -} - -.monaco-editor.hc-black .peekview-widget .head .peekview-title .filename { - color: #fff; -} - -.monaco-editor.hc-black .peekview-widget .head .peekview-title .dirname { - color: #fff; - opacity: 0.6; -} - -.monaco-editor.hc-black .peekview-widget > .body { - border-color: #6FC3DF; -} \ No newline at end of file diff --git a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts index 7da35776f32..4e6eb948929 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/peekViewWidget.ts @@ -20,6 +20,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IOptions, ZoneWidget } from './zoneWidget'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; export var IPeekViewService = createDecorator('peekViewService'); @@ -164,3 +166,30 @@ export abstract class PeekViewWidget extends ZoneWidget implements IPeekViewServ this._bodyElement.style.height = strings.format('{0}px', heightInPixel); } } + +// theming + +export const editorPeekTitleBackground = registerColor('editorPeekTitleBackground', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('editorPeekTitleBackground', 'Editor peek title area background')); +export const editorPeekTitle = registerColor('editorPeekTitle', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('editorPeekTitle', 'Editor peek title color')); +export const editorPeekTitleInfo = registerColor('editorPeekTitleInfo', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('editorPeekTitleInfo', 'Editor peek title info color')); +export const editorPeekBorders = registerColor('editorPeekBorder', { dark: '#007acc', light: '#007acc', hc: '#6FC3DF' }, nls.localize('editorPeekBorder', 'Editor peek view borders')); + +registerThemingParticipant((theme, collector) => { + let peekBackground = theme.getColor(editorPeekTitleBackground); + if (peekBackground) { + collector.addRule(`.monaco-editor.${theme.selector} .peekview-widget .head { background-color: ${peekBackground}; }`); + } + let title = theme.getColor(editorPeekTitle); + if (title) { + collector.addRule(`.monaco-editor.${theme.selector} .peekview-widget .head .peekview-title .filename { color: ${title}; }`); + } + let titleInfo = theme.getColor(editorPeekTitleInfo); + if (titleInfo) { + collector.addRule(`.monaco-editor.${theme.selector} .peekview-widget .head .peekview-title .dirname:not(:empty) { color: ${titleInfo}; }`); + } + let borders = theme.getColor(editorPeekBorders); + if (borders) { + collector.addRule(`.monaco-editor.${theme.selector} .zone-widget-container.peekview-widget { border-top-color: ${borders}; border-bottom-color: ${borders}; }`); + collector.addRule(`.monaco-editor.${theme.selector} .peekview-widget > .body { border-color: ${borders}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/editor/electron-browser/textMate/TMSyntax.ts b/src/vs/editor/electron-browser/textMate/TMSyntax.ts index 3d6125f036f..35b0adb215e 100644 --- a/src/vs/editor/electron-browser/textMate/TMSyntax.ts +++ b/src/vs/editor/electron-browser/textMate/TMSyntax.ts @@ -146,7 +146,7 @@ export class MainProcessTextMateSyntax implements ITextMateService { this._modeService.onDidCreateMode((mode) => { let modeId = mode.getId(); - if (this._languageToScope[modeId]) { + if (this._languageToScope.has(modeId)) { this.registerDefinition(modeId); } }); @@ -234,7 +234,7 @@ export class MainProcessTextMateSyntax implements ITextMateService { let modeId = syntax.language; if (modeId) { - this._languageToScope[modeId] = syntax.scopeName; + this._languageToScope.set(modeId, syntax.scopeName); } } @@ -257,7 +257,7 @@ export class MainProcessTextMateSyntax implements ITextMateService { } private _createGrammar(modeId: string): TPromise { - let scopeName = this._languageToScope[modeId]; + let scopeName = this._languageToScope.get(modeId); let languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); if (!languageRegistration) { // No TM grammar defined diff --git a/src/vs/editor/test/browser/standalone/simpleServices.test.ts b/src/vs/editor/test/browser/standalone/simpleServices.test.ts index 12755f6d097..14c53c99b5e 100644 --- a/src/vs/editor/test/browser/standalone/simpleServices.test.ts +++ b/src/vs/editor/test/browser/standalone/simpleServices.test.ts @@ -10,7 +10,7 @@ import { SimpleConfigurationService, SimpleMessageService, StandaloneKeybindingS import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IKeyboardEvent } from "vs/platform/keybinding/common/keybinding"; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; suite('StandaloneKeybindingService', () => { diff --git a/src/vs/editor/test/common/modes/supports/tokenization.test.ts b/src/vs/editor/test/common/modes/supports/tokenization.test.ts index 28187687942..199a670cdaf 100644 --- a/src/vs/editor/test/common/modes/supports/tokenization.test.ts +++ b/src/vs/editor/test/common/modes/supports/tokenization.test.ts @@ -5,13 +5,13 @@ 'use strict'; import * as assert from 'assert'; -import { strcmp, parseTheme, Theme, ParsedThemeRule, ColorMap, ExternalThemeTrieElement, ThemeTrieElementRule } from 'vs/editor/common/modes/supports/tokenization'; +import { strcmp, parseTokenTheme, TokenTheme, ParsedTokenThemeRule, ColorMap, ExternalThemeTrieElement, ThemeTrieElementRule } from 'vs/editor/common/modes/supports/tokenization'; import { FontStyle } from 'vs/editor/common/modes'; -suite('Theme matching', () => { +suite('Token theme matching', () => { test('gives higher priority to deeper matches', () => { - let theme = Theme.createFromRawTheme([ + let theme = TokenTheme.createFromRawTokenTheme([ { token: '', foreground: '100000', background: '200000' }, { token: 'punctuation.definition.string.begin.html', foreground: '300000' }, { token: 'punctuation.definition.string', foreground: '400000' }, @@ -29,7 +29,7 @@ suite('Theme matching', () => { }); test('can match', () => { - let theme = Theme.createFromRawTheme([ + let theme = TokenTheme.createFromRawTokenTheme([ { token: '', foreground: 'F8F8F2', background: '272822' }, { token: 'source', background: '100000' }, { token: 'something', background: '100000' }, @@ -121,11 +121,11 @@ suite('Theme matching', () => { }); }); -suite('Theme parsing', () => { +suite('Token theme parsing', () => { test('can parse', () => { - let actual = parseTheme([ + let actual = parseTokenTheme([ { token: '', foreground: 'F8F8F2', background: '272822' }, { token: 'source', background: '100000' }, { token: 'something', background: '100000' }, @@ -140,24 +140,24 @@ suite('Theme parsing', () => { ]); let expected = [ - new ParsedThemeRule('', 0, FontStyle.NotSet, 'F8F8F2', '272822'), - new ParsedThemeRule('source', 1, FontStyle.NotSet, null, '100000'), - new ParsedThemeRule('something', 2, FontStyle.NotSet, null, '100000'), - new ParsedThemeRule('bar', 3, FontStyle.NotSet, null, '010000'), - new ParsedThemeRule('baz', 4, FontStyle.NotSet, null, '010000'), - new ParsedThemeRule('bar', 5, FontStyle.Bold, null, null), - new ParsedThemeRule('constant', 6, FontStyle.Italic, 'ff0000', null), - new ParsedThemeRule('constant.numeric', 7, FontStyle.NotSet, '00ff00', null), - new ParsedThemeRule('constant.numeric.hex', 8, FontStyle.Bold, null, null), - new ParsedThemeRule('constant.numeric.oct', 9, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), - new ParsedThemeRule('constant.numeric.dec', 10, FontStyle.None, '0000ff', null), + new ParsedTokenThemeRule('', 0, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('source', 1, FontStyle.NotSet, null, '100000'), + new ParsedTokenThemeRule('something', 2, FontStyle.NotSet, null, '100000'), + new ParsedTokenThemeRule('bar', 3, FontStyle.NotSet, null, '010000'), + new ParsedTokenThemeRule('baz', 4, FontStyle.NotSet, null, '010000'), + new ParsedTokenThemeRule('bar', 5, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant', 6, FontStyle.Italic, 'ff0000', null), + new ParsedTokenThemeRule('constant.numeric', 7, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('constant.numeric.hex', 8, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant.numeric.oct', 9, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), + new ParsedTokenThemeRule('constant.numeric.dec', 10, FontStyle.None, '0000ff', null), ]; assert.deepEqual(actual, expected); }); }); -suite('Theme resolving', () => { +suite('Token theme resolving', () => { test('strcmp works', () => { let actual = ['bar', 'z', 'zu', 'a', 'ab', ''].sort(strcmp); @@ -167,7 +167,7 @@ suite('Theme resolving', () => { }); test('always has defaults', () => { - let actual = Theme.createFromParsedTheme([]); + let actual = TokenTheme.createFromParsedTokenTheme([]); let colorMap = new ColorMap(); const _A = colorMap.getId('000000'); const _B = colorMap.getId('ffffff'); @@ -176,8 +176,8 @@ suite('Theme resolving', () => { }); test('respects incoming defaults 1', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, null, null) + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, null) ]); let colorMap = new ColorMap(); const _A = colorMap.getId('000000'); @@ -187,8 +187,8 @@ suite('Theme resolving', () => { }); test('respects incoming defaults 2', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.None, null, null) + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.None, null, null) ]); let colorMap = new ColorMap(); const _A = colorMap.getId('000000'); @@ -198,8 +198,8 @@ suite('Theme resolving', () => { }); test('respects incoming defaults 3', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.Bold, null, null) + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null) ]); let colorMap = new ColorMap(); const _A = colorMap.getId('000000'); @@ -209,8 +209,8 @@ suite('Theme resolving', () => { }); test('respects incoming defaults 4', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, 'ff0000', null) + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'ff0000', null) ]); let colorMap = new ColorMap(); const _A = colorMap.getId('ff0000'); @@ -220,8 +220,8 @@ suite('Theme resolving', () => { }); test('respects incoming defaults 5', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, null, 'ff0000') + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000') ]); let colorMap = new ColorMap(); const _A = colorMap.getId('000000'); @@ -231,10 +231,10 @@ suite('Theme resolving', () => { }); test('can merge incoming defaults', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, null, 'ff0000'), - new ParsedThemeRule('', -1, FontStyle.NotSet, '00ff00', null), - new ParsedThemeRule('', -1, FontStyle.Bold, null, null), + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, null, 'ff0000'), + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('', -1, FontStyle.Bold, null, null), ]); let colorMap = new ColorMap(); const _A = colorMap.getId('00ff00'); @@ -244,9 +244,9 @@ suite('Theme resolving', () => { }); test('defaults are inherited', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), - new ParsedThemeRule('var', -1, FontStyle.NotSet, 'ff0000', null) + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.NotSet, 'ff0000', null) ]); let colorMap = new ColorMap(); const _A = colorMap.getId('F8F8F2'); @@ -260,10 +260,10 @@ suite('Theme resolving', () => { }); test('same rules get merged', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), - new ParsedThemeRule('var', 1, FontStyle.Bold, null, null), - new ParsedThemeRule('var', 0, FontStyle.NotSet, 'ff0000', null), + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', 1, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('var', 0, FontStyle.NotSet, 'ff0000', null), ]); let colorMap = new ColorMap(); const _A = colorMap.getId('F8F8F2'); @@ -277,10 +277,10 @@ suite('Theme resolving', () => { }); test('rules are inherited 1', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), - new ParsedThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), - new ParsedThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), + new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), ]); let colorMap = new ColorMap(); const _A = colorMap.getId('F8F8F2'); @@ -297,15 +297,15 @@ suite('Theme resolving', () => { }); test('rules are inherited 2', () => { - let actual = Theme.createFromParsedTheme([ - new ParsedThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), - new ParsedThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), - new ParsedThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), - new ParsedThemeRule('constant', 4, FontStyle.Italic, '100000', null), - new ParsedThemeRule('constant.numeric', 5, FontStyle.NotSet, '200000', null), - new ParsedThemeRule('constant.numeric.hex', 6, FontStyle.Bold, null, null), - new ParsedThemeRule('constant.numeric.oct', 7, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), - new ParsedThemeRule('constant.numeric.dec', 8, FontStyle.None, '300000', null), + let actual = TokenTheme.createFromParsedTokenTheme([ + new ParsedTokenThemeRule('', -1, FontStyle.NotSet, 'F8F8F2', '272822'), + new ParsedTokenThemeRule('var', -1, FontStyle.Bold, 'ff0000', null), + new ParsedTokenThemeRule('var.identifier', -1, FontStyle.NotSet, '00ff00', null), + new ParsedTokenThemeRule('constant', 4, FontStyle.Italic, '100000', null), + new ParsedTokenThemeRule('constant.numeric', 5, FontStyle.NotSet, '200000', null), + new ParsedTokenThemeRule('constant.numeric.hex', 6, FontStyle.Bold, null, null), + new ParsedTokenThemeRule('constant.numeric.oct', 7, FontStyle.Bold | FontStyle.Italic | FontStyle.Underline, null, null), + new ParsedTokenThemeRule('constant.numeric.dec', 8, FontStyle.None, '300000', null), ]); let colorMap = new ColorMap(); const _A = colorMap.getId('F8F8F2'); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 05a5de3ca24..ea616a45728 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -875,17 +875,22 @@ declare module monaco.editor { /** * Define a new theme. */ - export function defineTheme(themeName: string, themeData: ITheme): void; + export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void; export type BuiltinTheme = 'vs' | 'vs-dark' | 'hc-black'; - export interface ITheme { + export interface IStandaloneThemeData { base: BuiltinTheme; inherit: boolean; - rules: IThemeRule[]; + rules: ITokenThemeRule[]; + colors: IColors; } - export interface IThemeRule { + export type IColors = { + [colorId: string]: string; + }; + + export interface ITokenThemeRule { token: string; foreground?: string; background?: string; @@ -973,6 +978,7 @@ declare module monaco.editor { } export interface IEditorOverrideServices { + [index: string]: any; } /** diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index a8433b4ecea..e57ed6493ce 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -104,7 +104,7 @@ class MenuItemActionItem extends ActionItem { super(undefined, action, { icon: !!action.class, label: !action.class }); } - private get _command() { + private get _commandAction(): IAction { return this._wantsAltCommand && (this._action).alt || this._action; } @@ -112,7 +112,8 @@ class MenuItemActionItem extends ActionItem { event.preventDefault(); event.stopPropagation(); - this._command.run().done(undefined, err => this._messageService.show(Severity.Error, err)); + this.actionRunner.run(this._commandAction) + .done(undefined, err => this._messageService.show(Severity.Error, err)); } render(container: HTMLElement): void { @@ -149,29 +150,29 @@ class MenuItemActionItem extends ActionItem { _updateLabel(): void { if (this.options.label) { - this.$e.text(this._command.label); + this.$e.text(this._commandAction.label); } } _updateTooltip(): void { const element = this.$e.getHTMLElement(); - const keybinding = this._keybindingService.lookupKeybinding(this._command.id); + const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); const keybindingLabel = keybinding && keybinding.getLabel(); element.title = keybindingLabel - ? localize('titleAndKb', "{0} ({1})", this._command.label, keybindingLabel) - : this._command.label; + ? localize('titleAndKb', "{0} ({1})", this._commandAction.label, keybindingLabel) + : this._commandAction.label; } _updateClass(): void { if (this.options.icon) { const element = this.$e.getHTMLElement(); - if (this._command !== this._action) { + if (this._commandAction !== this._action) { element.classList.remove(this._action.class); } else if ((this._action).alt) { element.classList.remove((this._action).alt.class); } - element.classList.add('icon', this._command.class); + element.classList.add('icon', this._commandAction.class); } } } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 180007f4822..7c78678d45c 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -174,8 +174,12 @@ export class MenuItemAction extends ExecuteCommandAction { this.alt = alt ? new MenuItemAction(alt, undefined, arg, commandService) : undefined; } - run(): TPromise { - return super.run(this._arg); + run(...args: any[]): TPromise { + if (this._arg) { + return super.run(this._arg, ...args); + } else { + return super.run(...args); + } } } diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index 7692e7b5db4..c50b6a0877d 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -5,7 +5,7 @@ 'use strict'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TPromise } from 'vs/base/common/winjs.base'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; @@ -50,6 +50,7 @@ export interface IContextMenuDelegate { getKeyBinding?(action: IAction): ResolvedKeybinding; getMenuClassName?(): string; onHide?(didCancel: boolean): void; + actionRunner?: IActionRunner; } export class ContextSubMenu { diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index dcf4115539a..e6f52095891 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -12,7 +12,6 @@ import events = require('vs/base/common/events'); import { isLinux } from 'vs/base/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import Event from 'vs/base/common/event'; -import { Schemas } from 'vs/base/common/network'; import { equalsIgnoreCase, beginsWithIgnoreCase } from 'vs/base/common/strings'; export const IFileService = createDecorator('fileService'); @@ -232,10 +231,10 @@ export class FileChangesEvent extends events.Event { // For deleted also return true when deleted folder is parent of target path if (type === FileChangeType.DELETED) { - return isEqualOrParent(resource.fsPath, change.resource.fsPath); + return isEqualOrParent(resource.fsPath, change.resource.fsPath, !isLinux /* ignorecase */); } - return isEqual(resource.fsPath, change.resource.fsPath); + return isEqual(resource.fsPath, change.resource.fsPath, !isLinux /* ignorecase */); }); } @@ -292,48 +291,20 @@ export class FileChangesEvent extends events.Event { } } -export function isEqual(resourceA: URI, resourceB: URI): boolean; -export function isEqual(pathA: string, pathB: string): boolean; -export function isEqual(resourceOrPathA: string | URI, resourceOrPathB: string | URI): boolean { - const identityEquals = (resourceOrPathA === resourceOrPathB); - if (identityEquals) { - return true; - } - - if (!resourceOrPathA || !resourceOrPathB) { - return false; - } - - // Compare by URI - if (typeof resourceOrPathA !== 'string') { - const resourceA = resourceOrPathA; - const resourceB = resourceOrPathB as URI; - - if (resourceA.scheme !== resourceB.scheme) { - return false; - } - - // File URIs compare by fsPath - if (resourceA.scheme === Schemas.file) { - return isEqual(resourceA.fsPath, resourceB.fsPath); - } - - // Non-file URIs compare by full string - return resourceA.toString() === resourceB.toString(); - } - - // Compare by Path - const pathA = resourceOrPathA; - const pathB = resourceOrPathB as string; - - if (isLinux) { +export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean { + const identityEquals = (pathA === pathB); + if (!ignoreCase || identityEquals) { return identityEquals; } + if (!pathA || !pathB) { + return false; + } + return equalsIgnoreCase(pathA, pathB); } -export function isParent(path: string, candidate: string): boolean { +export function isParent(path: string, candidate: string, ignoreCase?: boolean): boolean { if (!path || !candidate || path === candidate) { return false; } @@ -346,14 +317,14 @@ export function isParent(path: string, candidate: string): boolean { candidate += paths.nativeSep; } - if (!isLinux) { + if (ignoreCase) { return beginsWithIgnoreCase(path, candidate); } return path.indexOf(candidate) === 0; } -export function isEqualOrParent(path: string, candidate: string): boolean { +export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean): boolean { if (path === candidate) { return true; } @@ -366,7 +337,7 @@ export function isEqualOrParent(path: string, candidate: string): boolean { return false; } - if (!isLinux) { + if (ignoreCase) { const beginsWith = beginsWithIgnoreCase(path, candidate); if (!beginsWith) { return false; @@ -391,7 +362,7 @@ export function isEqualOrParent(path: string, candidate: string): boolean { return path.indexOf(candidate) === 0; } -export function indexOf(path: string, candidate: string): number { +export function indexOf(path: string, candidate: string, ignoreCase?: boolean): number { if (candidate.length > path.length) { return -1; } @@ -400,7 +371,7 @@ export function indexOf(path: string, candidate: string): number { return 0; } - if (!isLinux) { + if (ignoreCase) { path = path.toLowerCase(); candidate = candidate.toLowerCase(); } diff --git a/src/vs/platform/files/test/files.test.ts b/src/vs/platform/files/test/files.test.ts index 01f0313a521..14f4643f749 100644 --- a/src/vs/platform/files/test/files.test.ts +++ b/src/vs/platform/files/test/files.test.ts @@ -47,206 +47,156 @@ suite('Files', () => { assert.strictEqual(true, r1.gotDeleted()); }); - function testIsEqual(testMethod: (pA: string, pB: string) => boolean): void { + function testIsEqual(testMethod: (pA: string, pB: string, ignoreCase: boolean) => boolean): void { // corner cases - assert(testMethod('', '')); - assert(!testMethod(null, '')); - assert(!testMethod(void 0, '')); + assert(testMethod('', '', true)); + assert(!testMethod(null, '', true)); + assert(!testMethod(void 0, '', true)); // basics (string) - assert(testMethod('/', '/')); - assert(testMethod('/some', '/some')); - assert(testMethod('/some/path', '/some/path')); + assert(testMethod('/', '/', true)); + assert(testMethod('/some', '/some', true)); + assert(testMethod('/some/path', '/some/path', true)); - assert(testMethod('c:\\', 'c:\\')); - assert(testMethod('c:\\some', 'c:\\some')); - assert(testMethod('c:\\some\\path', 'c:\\some\\path')); + assert(testMethod('c:\\', 'c:\\', true)); + assert(testMethod('c:\\some', 'c:\\some', true)); + assert(testMethod('c:\\some\\path', 'c:\\some\\path', true)); - assert(testMethod('/someöäü/path', '/someöäü/path')); - assert(testMethod('c:\\someöäü\\path', 'c:\\someöäü\\path')); + assert(testMethod('/someöäü/path', '/someöäü/path', true)); + assert(testMethod('c:\\someöäü\\path', 'c:\\someöäü\\path', true)); - assert(!testMethod('/some/path', '/some/other/path')); - assert(!testMethod('c:\\some\\path', 'c:\\some\\other\\path')); - assert(!testMethod('c:\\some\\path', 'd:\\some\\path')); + assert(!testMethod('/some/path', '/some/other/path', true)); + assert(!testMethod('c:\\some\\path', 'c:\\some\\other\\path', true)); + assert(!testMethod('c:\\some\\path', 'd:\\some\\path', true)); - // case insensitive (unless isLinux) - if (isLinux) { - assert(!testMethod('/some/path', '/some/PATH')); - assert(!testMethod('/some/path', '/some/other/PATH')); - } else { - assert(testMethod('/some/path', '/some/PATH')); - assert(testMethod('/someöäü/path', '/someÖÄÜ/PATH')); - assert(testMethod('c:\\some\\path', 'c:\\some\\PATH')); - assert(testMethod('c:\\someöäü\\path', 'c:\\someÖÄÜ\\PATH')); - assert(testMethod('c:\\some\\path', 'C:\\some\\PATH')); - } + assert(testMethod('/some/path', '/some/PATH', true)); + assert(testMethod('/someöäü/path', '/someÖÄÜ/PATH', true)); + assert(testMethod('c:\\some\\path', 'c:\\some\\PATH', true)); + assert(testMethod('c:\\someöäü\\path', 'c:\\someÖÄÜ\\PATH', true)); + assert(testMethod('c:\\some\\path', 'C:\\some\\PATH', true)); } - test('isEqual', function () { + test('isEqual (ignoreCase)', function () { testIsEqual(isEqual); // basics (uris) - assert(isEqual(URI.file('/some/path'), URI.file('/some/path'))); - assert(isEqual(URI.file('c:\\some\\path'), URI.file('c:\\some\\path'))); + assert(isEqual(URI.file('/some/path').fsPath, URI.file('/some/path').fsPath, true)); + assert(isEqual(URI.file('c:\\some\\path').fsPath, URI.file('c:\\some\\path').fsPath, true)); - assert(isEqual(URI.file('/someöäü/path'), URI.file('/someöäü/path'))); - assert(isEqual(URI.file('c:\\someöäü\\path'), URI.file('c:\\someöäü\\path'))); + assert(isEqual(URI.file('/someöäü/path').fsPath, URI.file('/someöäü/path').fsPath, true)); + assert(isEqual(URI.file('c:\\someöäü\\path').fsPath, URI.file('c:\\someöäü\\path').fsPath, true)); - assert(!isEqual(URI.file('/some/path'), URI.file('/some/other/path'))); - assert(!isEqual(URI.file('c:\\some\\path'), URI.file('c:\\some\\other\\path'))); + assert(!isEqual(URI.file('/some/path').fsPath, URI.file('/some/other/path').fsPath, true)); + assert(!isEqual(URI.file('c:\\some\\path').fsPath, URI.file('c:\\some\\other\\path').fsPath, true)); - assert(isEqual(URI.parse('some://cool/uri'), URI.parse('some://cool/uri'))); - assert(!isEqual(URI.parse('some://cool/uri'), URI.parse('some://other/uri'))); - - // case insensitive (unless isLinux) - if (isLinux) { - assert(!isEqual(URI.file('/some/path'), URI.file('/some/PATH'))); - } else { - assert(isEqual(URI.file('/some/path'), URI.file('/some/PATH'))); - assert(isEqual(URI.file('/someöäü/path'), URI.file('/someÖÄÜ/PATH'))); - assert(isEqual(URI.file('c:\\some\\path'), URI.file('c:\\some\\PATH'))); - assert(isEqual(URI.file('c:\\someöäü\\path'), URI.file('c:\\someÖÄÜ\\PATH'))); - assert(isEqual(URI.file('c:\\some\\path'), URI.file('C:\\some\\PATH'))); - } + assert(isEqual(URI.file('/some/path').fsPath, URI.file('/some/PATH').fsPath, true)); + assert(isEqual(URI.file('/someöäü/path').fsPath, URI.file('/someÖÄÜ/PATH').fsPath, true)); + assert(isEqual(URI.file('c:\\some\\path').fsPath, URI.file('c:\\some\\PATH').fsPath, true)); + assert(isEqual(URI.file('c:\\someöäü\\path').fsPath, URI.file('c:\\someÖÄÜ\\PATH').fsPath, true)); + assert(isEqual(URI.file('c:\\some\\path').fsPath, URI.file('C:\\some\\PATH').fsPath, true)); }); - test('isParent', function () { + test('isParent (ignorecase)', function () { if (isWindows) { - assert(isParent('c:\\some\\path', 'c:\\')); - assert(isParent('c:\\some\\path', 'c:\\some')); - assert(isParent('c:\\some\\path', 'c:\\some\\')); - assert(isParent('c:\\someöäü\\path', 'c:\\someöäü')); - assert(isParent('c:\\someöäü\\path', 'c:\\someöäü\\')); - assert(isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar')); - assert(isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\')); + assert(isParent('c:\\some\\path', 'c:\\', true)); + assert(isParent('c:\\some\\path', 'c:\\some', true)); + assert(isParent('c:\\some\\path', 'c:\\some\\', true)); + assert(isParent('c:\\someöäü\\path', 'c:\\someöäü', true)); + assert(isParent('c:\\someöäü\\path', 'c:\\someöäü\\', true)); + assert(isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar', true)); + assert(isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\', true)); - assert(isParent('c:\\some\\path', 'C:\\')); - assert(isParent('c:\\some\\path', 'c:\\SOME')); - assert(isParent('c:\\some\\path', 'c:\\SOME\\')); + assert(isParent('c:\\some\\path', 'C:\\', true)); + assert(isParent('c:\\some\\path', 'c:\\SOME', true)); + assert(isParent('c:\\some\\path', 'c:\\SOME\\', true)); - assert(!isParent('c:\\some\\path', 'd:\\')); - assert(!isParent('c:\\some\\path', 'c:\\some\\path')); - assert(!isParent('c:\\some\\path', 'd:\\some\\path')); - assert(!isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\barr')); - assert(!isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test')); + assert(!isParent('c:\\some\\path', 'd:\\', true)); + assert(!isParent('c:\\some\\path', 'c:\\some\\path', true)); + assert(!isParent('c:\\some\\path', 'd:\\some\\path', true)); + assert(!isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\barr', true)); + assert(!isParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test', true)); } - if (isMacintosh) { - assert(isParent('/some/path', '/')); - assert(isParent('/some/path', '/some')); - assert(isParent('/some/path', '/some/')); - assert(isParent('/someöäü/path', '/someöäü')); - assert(isParent('/someöäü/path', '/someöäü/')); - assert(isParent('/foo/bar/test.ts', '/foo/bar')); - assert(isParent('/foo/bar/test.ts', '/foo/bar/')); + if (isMacintosh || isLinux) { + assert(isParent('/some/path', '/', true)); + assert(isParent('/some/path', '/some', true)); + assert(isParent('/some/path', '/some/', true)); + assert(isParent('/someöäü/path', '/someöäü', true)); + assert(isParent('/someöäü/path', '/someöäü/', true)); + assert(isParent('/foo/bar/test.ts', '/foo/bar', true)); + assert(isParent('/foo/bar/test.ts', '/foo/bar/', true)); - assert(isParent('/some/path', '/SOME')); - assert(isParent('/some/path', '/SOME/')); - assert(isParent('/someöäü/path', '/SOMEÖÄÜ')); - assert(isParent('/someöäü/path', '/SOMEÖÄÜ/')); + assert(isParent('/some/path', '/SOME', true)); + assert(isParent('/some/path', '/SOME/', true)); + assert(isParent('/someöäü/path', '/SOMEÖÄÜ', true)); + assert(isParent('/someöäü/path', '/SOMEÖÄÜ/', true)); - assert(!isParent('/some/path', '/some/path')); - assert(!isParent('/foo/bar/test.ts', '/foo/barr')); - assert(!isParent('/foo/bar/test.ts', '/foo/bar/test')); - } - - if (isLinux) { - assert(isParent('/some/path', '/')); - assert(isParent('/some/path', '/some')); - assert(isParent('/some/path', '/some/')); - assert(isParent('/someöäü/path', '/someöäü')); - assert(isParent('/someöäü/path', '/someöäü/')); - assert(isParent('/foo/bar/test.ts', '/foo/bar')); - assert(isParent('/foo/bar/test.ts', '/foo/bar/')); - - assert(!isParent('/some/path', '/SOME')); - - assert(!isParent('/some/path', '/some/path')); - assert(!isParent('/foo/bar/test.ts', '/foo/barr')); - assert(!isParent('/foo/bar/test.ts', '/foo/bar/test')); + assert(!isParent('/some/path', '/some/path', true)); + assert(!isParent('/foo/bar/test.ts', '/foo/barr', true)); + assert(!isParent('/foo/bar/test.ts', '/foo/bar/test', true)); } }); - test('isEqualOrParent', function () { + test('isEqualOrParent (ignorecase)', function () { // same assertions apply as with isEqual() testIsEqual(isEqualOrParent); if (isWindows) { - assert(isEqualOrParent('c:\\some\\path', 'c:\\')); - assert(isEqualOrParent('c:\\some\\path', 'c:\\some')); - assert(isEqualOrParent('c:\\some\\path', 'c:\\some\\')); - assert(isEqualOrParent('c:\\someöäü\\path', 'c:\\someöäü')); - assert(isEqualOrParent('c:\\someöäü\\path', 'c:\\someöäü\\')); - assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar')); - assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\')); - assert(isEqualOrParent('c:\\some\\path', 'c:\\some\\path')); - assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test.ts')); + assert(isEqualOrParent('c:\\some\\path', 'c:\\', true)); + assert(isEqualOrParent('c:\\some\\path', 'c:\\some', true)); + assert(isEqualOrParent('c:\\some\\path', 'c:\\some\\', true)); + assert(isEqualOrParent('c:\\someöäü\\path', 'c:\\someöäü', true)); + assert(isEqualOrParent('c:\\someöäü\\path', 'c:\\someöäü\\', true)); + assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar', true)); + assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\', true)); + assert(isEqualOrParent('c:\\some\\path', 'c:\\some\\path', true)); + assert(isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test.ts', true)); - assert(isEqualOrParent('c:\\some\\path', 'C:\\')); - assert(isEqualOrParent('c:\\some\\path', 'c:\\SOME')); - assert(isEqualOrParent('c:\\some\\path', 'c:\\SOME\\')); + assert(isEqualOrParent('c:\\some\\path', 'C:\\', true)); + assert(isEqualOrParent('c:\\some\\path', 'c:\\SOME', true)); + assert(isEqualOrParent('c:\\some\\path', 'c:\\SOME\\', true)); - assert(!isEqualOrParent('c:\\some\\path', 'd:\\')); - assert(!isEqualOrParent('c:\\some\\path', 'd:\\some\\path')); - assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\barr')); - assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test')); - assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test.')); - assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\BAR\\test.')); + assert(!isEqualOrParent('c:\\some\\path', 'd:\\', true)); + assert(!isEqualOrParent('c:\\some\\path', 'd:\\some\\path', true)); + assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\barr', true)); + assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test', true)); + assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\bar\\test.', true)); + assert(!isEqualOrParent('c:\\foo\\bar\\test.ts', 'c:\\foo\\BAR\\test.', true)); } - if (isMacintosh) { - assert(isEqualOrParent('/some/path', '/')); - assert(isEqualOrParent('/some/path', '/some')); - assert(isEqualOrParent('/some/path', '/some/')); - assert(isEqualOrParent('/someöäü/path', '/someöäü')); - assert(isEqualOrParent('/someöäü/path', '/someöäü/')); - assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar')); - assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar/')); - assert(isEqualOrParent('/some/path', '/some/path')); + if (isMacintosh || isLinux) { + assert(isEqualOrParent('/some/path', '/', true)); + assert(isEqualOrParent('/some/path', '/some', true)); + assert(isEqualOrParent('/some/path', '/some/', true)); + assert(isEqualOrParent('/someöäü/path', '/someöäü', true)); + assert(isEqualOrParent('/someöäü/path', '/someöäü/', true)); + assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar', true)); + assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar/', true)); + assert(isEqualOrParent('/some/path', '/some/path', true)); - assert(isEqualOrParent('/some/path', '/SOME')); - assert(isEqualOrParent('/some/path', '/SOME/')); - assert(isEqualOrParent('/someöäü/path', '/SOMEÖÄÜ')); - assert(isEqualOrParent('/someöäü/path', '/SOMEÖÄÜ/')); + assert(isEqualOrParent('/some/path', '/SOME', true)); + assert(isEqualOrParent('/some/path', '/SOME/', true)); + assert(isEqualOrParent('/someöäü/path', '/SOMEÖÄÜ', true)); + assert(isEqualOrParent('/someöäü/path', '/SOMEÖÄÜ/', true)); - assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/barr')); - assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/bar/test')); - assert(!isEqualOrParent('foo/bar/test.ts', 'foo/bar/test.')); - assert(!isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.')); - } - - if (isLinux) { - assert(isEqualOrParent('/some/path', '/')); - assert(isEqualOrParent('/some/path', '/some')); - assert(isEqualOrParent('/some/path', '/some/')); - assert(isEqualOrParent('/someöäü/path', '/someöäü')); - assert(isEqualOrParent('/someöäü/path', '/someöäü/')); - assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar')); - assert(isEqualOrParent('/foo/bar/test.ts', '/foo/bar/')); - assert(isEqualOrParent('/some/path', '/some/path')); - - assert(!isEqualOrParent('/some/path', '/SOME')); - - assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/barr')); - assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/bar/test')); + assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/barr', true)); + assert(!isEqualOrParent('/foo/bar/test.ts', '/foo/bar/test', true)); + assert(!isEqualOrParent('foo/bar/test.ts', 'foo/bar/test.', true)); + assert(!isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.', true)); } }); - test('indexOf', function () { - assert.equal(indexOf('/some/path', '/some/path'), 0); - assert.equal(indexOf('/some/path/more', '/some/path'), 0); + test('indexOf (ignorecase)', function () { + assert.equal(indexOf('/some/path', '/some/path', true), 0); + assert.equal(indexOf('/some/path/more', '/some/path', true), 0); - assert.equal(indexOf('c:\\some\\path', 'c:\\some\\path'), 0); - assert.equal(indexOf('c:\\some\\path\\more', 'c:\\some\\path'), 0); + assert.equal(indexOf('c:\\some\\path', 'c:\\some\\path', true), 0); + assert.equal(indexOf('c:\\some\\path\\more', 'c:\\some\\path', true), 0); - assert.equal(indexOf('/some/path', '/some/other/path'), -1); + assert.equal(indexOf('/some/path', '/some/other/path', true), -1); - if (isLinux) { - assert.equal(indexOf('/some/path', '/some/PATH'), -1); - } else { - assert.equal(indexOf('/some/path', '/some/PATH'), 0); - } + assert.equal(indexOf('/some/path', '/some/PATH', true), 0); }); }); \ No newline at end of file diff --git a/src/vs/platform/instantiation/common/instantiation.ts b/src/vs/platform/instantiation/common/instantiation.ts index 6b44bbf29e6..17eae95cce9 100644 --- a/src/vs/platform/instantiation/common/instantiation.ts +++ b/src/vs/platform/instantiation/common/instantiation.ts @@ -41,23 +41,23 @@ export interface IConstructorSignature3 { } export interface IConstructorSignature4 { - new (first: A1, second: A2, third: A3, forth: A4, ...services: { _serviceBrand: any; }[]): T; + new (first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature5 { - new (first: A1, second: A2, third: A3, forth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; + new (first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature6 { - new (first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; + new (first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature7 { - new (first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; + new (first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature8 { - new (first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; + new (first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; } export interface ServicesAccessor { @@ -81,23 +81,23 @@ export interface IFunctionSignature3 { } export interface IFunctionSignature4 { - (accessor: ServicesAccessor, first: A1, second: A2, third: A3, forth: A4): R; + (accessor: ServicesAccessor, first: A1, second: A2, third: A3, fourth: A4): R; } export interface IFunctionSignature5 { - (accessor: ServicesAccessor, first: A1, second: A2, third: A3, forth: A4, fifth: A5): R; + (accessor: ServicesAccessor, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): R; } export interface IFunctionSignature6 { - (accessor: ServicesAccessor, first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6): R; + (accessor: ServicesAccessor, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): R; } export interface IFunctionSignature7 { - (accessor: ServicesAccessor, first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6, seventh: A7): R; + (accessor: ServicesAccessor, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): R; } export interface IFunctionSignature8 { - (accessor: ServicesAccessor, first: A1, second: A2, third: A3, forth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): R; + (accessor: ServicesAccessor, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): R; } export const IInstantiationService = createDecorator('instantiationService'); diff --git a/src/vs/platform/integrity/node/integrityServiceImpl.ts b/src/vs/platform/integrity/node/integrityServiceImpl.ts index 30078cac385..1800587520e 100644 --- a/src/vs/platform/integrity/node/integrityServiceImpl.ts +++ b/src/vs/platform/integrity/node/integrityServiceImpl.ts @@ -141,6 +141,7 @@ export class IntegrityServiceImpl implements IIntegrityService { for (let i = 0, len = allResults.length; isPure && i < len; i++) { if (!allResults[i].isPure) { isPure = false; + break; } } diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index 14a1934393e..7b30b0bb2f9 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -32,7 +32,6 @@ export interface IProductConfiguration { welcomePage: string; enableTelemetry: boolean; aiConfig: { - key: string; asimovKey: string; }; sendASmile: { diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 12bc1e8d2e0..ab229c7bc7a 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -19,7 +19,6 @@ export type ThemeType = 'light' | 'dark' | 'hc'; export interface ITheme { readonly selector: string; - readonly label: string; readonly type: ThemeType; /** diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 611e8a42f52..1218ddd4da8 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -8,6 +8,7 @@ import URI from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import paths = require('vs/base/common/paths'); import { isEqualOrParent } from 'vs/platform/files/common/files'; +import { isLinux } from 'vs/base/common/platform'; export const IWorkspaceContextService = createDecorator('contextService'); @@ -84,7 +85,7 @@ export class WorkspaceContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return isEqualOrParent(resource.fsPath, this.workspace.resource.fsPath); + return isEqualOrParent(resource.fsPath, this.workspace.resource.fsPath, !isLinux /* ignorecase */); } return false; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 0dc764fd500..562e241bdb8 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2395,11 +2395,12 @@ declare module 'vscode' { Unit = 10, Value = 11, Enum = 12, + EnumMember = 19, Keyword = 13, Snippet = 14, Color = 15, - File = 16, Reference = 17, + File = 16, Folder = 18 } @@ -4053,15 +4054,18 @@ declare module 'vscode' { export let textDocuments: TextDocument[]; /** - * Opens the denoted document from disk. Will return early if the - * document is already open, otherwise the document is loaded and the - * [open document](#workspace.onDidOpenTextDocument)-event fires. - * The document to open is denoted by the [uri](#Uri). Two schemes are supported: + * Opens a document. Will return early if this document is already open. Otherwise + * the document is loaded and the [didOpen](#workspace.onDidOpenTextDocument)-event fires. * - * file: A file on disk, will be rejected if the file does not exist or cannot be loaded, e.g. `file:///Users/frodo/r.ini`. - * untitled: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language will be derived from the file name. + * The document is denoted by an [uri](#Uri). Depending on the [scheme](#Uri.scheme) the + * following rules apply: + * * `file`-scheme: Open a file on disk, will be rejected if the file does not exist or cannot be loaded. + * * `untitled`-scheme: A new file that should be saved on disk, e.g. `untitled:c:\frodo\new.js`. The language + * will be derived from the file name. + * * For all other schemes the registered text document content [providers](#TextDocumentContentProvider) are consulted. * - * Uris with other schemes will make this method return a rejected promise. + * *Note* that the lifecycle of the returned document is owned by the editor and not by the extension. That means an + * [`onDidClose`](#workspace.onDidCloseTextDocument)-event can occur at any time after opening it. * * @param uri Identifies the resource to open. * @return A promise that resolves to a [document](#TextDocument). diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 2be1d58db17..8541610e298 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -171,18 +171,7 @@ export class ExtHostApiCommands { description: ` Render the html of the resource in an editor view. - Links contained in the document will be handled by VS Code whereby it supports \`file\`-resources and - [virtual](https://github.com/Microsoft/vscode/blob/master/src/vs/vscode.d.ts#L3295)-resources - as well as triggering commands using the \`command\`-scheme. Use the query part of a command-uri to pass along JSON-encoded - arguments - note that URL-encoding must be applied. The snippet below defines a command-link that calls the _previewHtml_ - command and passes along an uri: - \`\`\` - let href = encodeURI('command:vscode.previewHtml?' + JSON.stringify(someUri)); - let html = 'Show Resource....'; - \`\`\` - - The body element of the displayed html is dynamically annotated with one of the following css classes in order to - communicate the kind of color theme vscode is currently using: \`vscode-light\`, \`vscode-dark\`, or \`vscode-high-contrast\'. + See [working with the html preview](https://code.visualstudio.com/docs/extensionAPI/vscode-api-commands#working-with-the-html-preview) for more information about the html preview's intergration with the editor and for best practices for extension authors. `, args: [ { name: 'uri', description: 'Uri of the resource to preview.', constraint: value => value instanceof URI || typeof value === 'string' }, diff --git a/src/vs/workbench/api/node/extHostDocumentData.ts b/src/vs/workbench/api/node/extHostDocumentData.ts index 29735c3ac8f..ee8d0bc2d8d 100644 --- a/src/vs/workbench/api/node/extHostDocumentData.ts +++ b/src/vs/workbench/api/node/extHostDocumentData.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import { ok } from 'vs/base/common/assert'; import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; import { MirrorModel2 } from 'vs/editor/common/model/mirrorModel2'; import URI from 'vs/base/common/uri'; @@ -12,6 +13,7 @@ import * as vscode from 'vscode'; import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { MainThreadDocumentsShape } from './extHost.protocol'; import { ITextSource } from 'vs/editor/common/model/textSource'; +import { TPromise } from 'vs/base/common/winjs.base'; const _modeId2WordDefinition = new Map(); export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void { @@ -26,23 +28,26 @@ export class ExtHostDocumentData extends MirrorModel2 { private _proxy: MainThreadDocumentsShape; private _languageId: string; private _isDirty: boolean; - private _textLines: vscode.TextLine[]; private _document: vscode.TextDocument; + private _textLines: vscode.TextLine[] = []; + private _isDisposed: boolean = false; constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string, - languageId: string, versionId: number, isDirty: boolean) { - + languageId: string, versionId: number, isDirty: boolean + ) { super(uri, lines, eol, versionId); this._proxy = proxy; this._languageId = languageId; this._isDirty = isDirty; - this._textLines = []; } dispose(): void { - this._textLines.length = 0; + // we don't really dispose documents but let + // extensions still read from them. some + // operations, live saving, will now error tho + ok(!this._isDisposed); + this._isDisposed = true; this._isDirty = false; - super.dispose(); } equalLines({ lines }: ITextSource): boolean { @@ -68,30 +73,39 @@ export class ExtHostDocumentData extends MirrorModel2 { get languageId() { return data._languageId; }, get version() { return data._versionId; }, get isDirty() { return data._isDirty; }, - save() { return data._proxy.$trySaveDocument(data._uri); }, + save() { return data._save(); }, getText(range?) { return range ? data._getTextInRange(range) : data.getText(); }, get lineCount() { return data._lines.length; }, - lineAt(lineOrPos) { return data.lineAt(lineOrPos); }, - offsetAt(pos) { return data.offsetAt(pos); }, - positionAt(offset) { return data.positionAt(offset); }, - validateRange(ran) { return data.validateRange(ran); }, - validatePosition(pos) { return data.validatePosition(pos); }, - getWordRangeAtPosition(pos, regexp?) { return data.getWordRangeAtPosition(pos, regexp); } + lineAt(lineOrPos) { return data._lineAt(lineOrPos); }, + offsetAt(pos) { return data._offsetAt(pos); }, + positionAt(offset) { return data._positionAt(offset); }, + validateRange(ran) { return data._validateRange(ran); }, + validatePosition(pos) { return data._validatePosition(pos); }, + getWordRangeAtPosition(pos, regexp?) { return data._getWordRangeAtPosition(pos, regexp); } }; } - return this._document; + return Object.freeze(this._document); } _acceptLanguageId(newLanguageId: string): void { + ok(!this._isDisposed); this._languageId = newLanguageId; } _acceptIsDirty(isDirty: boolean): void { + ok(!this._isDisposed); this._isDirty = isDirty; } + private _save(): TPromise { + if (this._isDisposed) { + return TPromise.wrapError('Document has been closed'); + } + return this._proxy.$trySaveDocument(this._uri); + } + private _getTextInRange(_range: vscode.Range): string { - let range = this.validateRange(_range); + let range = this._validateRange(_range); if (range.isEmpty) { return ''; @@ -115,7 +129,7 @@ export class ExtHostDocumentData extends MirrorModel2 { return resultLines.join(lineEnding); } - lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine { + private _lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine { let line: number; if (lineOrPosition instanceof Position) { @@ -153,13 +167,13 @@ export class ExtHostDocumentData extends MirrorModel2 { return result; } - offsetAt(position: vscode.Position): number { - position = this.validatePosition(position); + private _offsetAt(position: vscode.Position): number { + position = this._validatePosition(position); this._ensureLineStarts(); return this._lineStarts.getAccumulatedValue(position.line - 1) + position.character; } - positionAt(offset: number): vscode.Position { + private _positionAt(offset: number): vscode.Position { offset = Math.floor(offset); offset = Math.max(0, offset); @@ -174,13 +188,13 @@ export class ExtHostDocumentData extends MirrorModel2 { // ---- range math - validateRange(range: vscode.Range): vscode.Range { + private _validateRange(range: vscode.Range): vscode.Range { if (!(range instanceof Range)) { throw new Error('Invalid argument'); } - let start = this.validatePosition(range.start); - let end = this.validatePosition(range.end); + let start = this._validatePosition(range.start); + let end = this._validatePosition(range.end); if (start === range.start && end === range.end) { return range; @@ -188,7 +202,7 @@ export class ExtHostDocumentData extends MirrorModel2 { return new Range(start.line, start.character, end.line, end.character); } - validatePosition(position: vscode.Position): vscode.Position { + private _validatePosition(position: vscode.Position): vscode.Position { if (!(position instanceof Position)) { throw new Error('Invalid argument'); } @@ -224,8 +238,8 @@ export class ExtHostDocumentData extends MirrorModel2 { return new Position(line, character); } - getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range { - let position = this.validatePosition(_position); + private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range { + let position = this._validatePosition(_position); if (!regexp || regExpLeadsToEndlessLoop(regexp)) { regexp = getWordDefinitionFor(this._languageId); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 22bfbfbd348..a3e32a301ff 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -30,14 +30,14 @@ export interface SelectionLike extends RangeLike { } export function toSelection(selection: ISelection): types.Selection { - let {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn} = selection; + let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection; let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1); let end = new types.Position(positionLineNumber - 1, positionColumn - 1); return new types.Selection(start, end); } export function fromSelection(selection: SelectionLike): ISelection { - let {anchor, active} = selection; + let { anchor, active } = selection; return { selectionStartLineNumber: anchor.line + 1, selectionStartColumn: anchor.character + 1, @@ -47,7 +47,7 @@ export function fromSelection(selection: SelectionLike): ISelection { } export function fromRange(range: RangeLike): IRange { - let {start, end} = range; + let { start, end } = range; return { startLineNumber: start.line + 1, startColumn: start.character + 1, @@ -57,7 +57,7 @@ export function fromRange(range: RangeLike): IRange { } export function toRange(range: IRange): types.Range { - let {startLineNumber, startColumn, endLineNumber, endColumn} = range; + let { startLineNumber, startColumn, endLineNumber, endColumn } = range; return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1); } @@ -252,6 +252,7 @@ export const CompletionItemKind = { case types.CompletionItemKind.Unit: return 'unit'; case types.CompletionItemKind.Value: return 'value'; case types.CompletionItemKind.Enum: return 'enum'; + case types.CompletionItemKind.EnumMember: return 'enum-member'; case types.CompletionItemKind.Keyword: return 'keyword'; case types.CompletionItemKind.Snippet: return 'snippet'; case types.CompletionItemKind.Text: return 'text'; diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 7f28a316015..b9bbdf3d03a 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -66,7 +66,7 @@ export class Position { if (other instanceof Position) { return true; } - let {line, character} = other; + let { line, character } = other; if (typeof line === 'number' && typeof character === 'number') { return true; } @@ -836,6 +836,7 @@ export enum CompletionItemKind { Unit = 10, Value = 11, Enum = 12, + EnumMember = 19, Keyword = 13, Snippet = 14, Color = 15, diff --git a/src/vs/workbench/api/node/mainThreadDocuments.ts b/src/vs/workbench/api/node/mainThreadDocuments.ts index 6e0e80af9db..8ec1335287c 100644 --- a/src/vs/workbench/api/node/mainThreadDocuments.ts +++ b/src/vs/workbench/api/node/mainThreadDocuments.ts @@ -7,7 +7,6 @@ import URI from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { EmitterEvent } from 'vs/base/common/eventEmitter'; -import { setDisposableTimeout } from 'vs/base/common/async'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; @@ -25,48 +24,47 @@ import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextEditorModel } from 'vs/workbench/common/editor'; -class TimeoutReference { +export class BoundModelReferenceCollection { - private static _delay = 1000 * 60 * 3; - - private _timer: IDisposable; - private _disposed = false; + private _data = new Array<{ length: number, dispose(): void }>(); + private _length = 0; constructor( - readonly codeEditorService: ICodeEditorService, - readonly editorGroupService: IEditorGroupService, - readonly reference: IReference + private _maxAge: number = 1000 * 60 * 3, + private _maxLength: number = 1024 * 1024 * 80 ) { - - const check = () => { - if (!this.isUsed()) { - this.dispose(); - } else { - this._timer = setDisposableTimeout(check, TimeoutReference._delay); - } - }; - this._timer = setDisposableTimeout(check, TimeoutReference._delay); + // } dispose(): void { - if (!this._disposed) { - this._disposed = true; - dispose(this.reference, this._timer); - } + this._data = dispose(this._data); } - private isUsed(): boolean { - for (const editor of this.codeEditorService.listCodeEditors()) { - if (editor.getModel() === this.reference.object.textEditorModel) { - return true; + add(ref: IReference): void { + let length = ref.object.textEditorModel.getValueLength(); + let handle: number; + let entry: { length: number, dispose(): void }; + const dispose = () => { + let idx = this._data.indexOf(entry); + if (idx >= 0) { + this._length -= length; + ref.dispose(); + clearTimeout(handle); + this._data.splice(idx, 1); } + }; + handle = setTimeout(dispose, this._maxAge); + entry = { length, dispose }; + + this._data.push(entry); + this._length += length; + this._cleanup(); + } + + private _cleanup(): void { + while (this._length > this._maxLength) { + this._data[0].dispose(); } - for (const group of this.editorGroupService.getStacksModel().groups) { - if (group.contains(this.reference.object.textEditorModel.uri)) { - return true; - } - } - return false; } } @@ -86,6 +84,7 @@ export class MainThreadDocuments extends MainThreadDocumentsShape { private _proxy: ExtHostDocumentsShape; private _modelIsSynced: { [modelId: string]: boolean; }; private _resourceContentProvider: { [handle: number]: IDisposable }; + private _modelReferenceCollection = new BoundModelReferenceCollection(); constructor( documentsAndEditors: MainThreadDocumentsAndEditors, @@ -113,6 +112,7 @@ export class MainThreadDocuments extends MainThreadDocumentsShape { this._modelIsSynced = {}; this._toDispose = []; + this._toDispose.push(this._modelReferenceCollection); this._toDispose.push(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this))); this._toDispose.push(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this))); modelService.onModelModeChanged(this._onModelModeChanged, this, this._toDispose); @@ -234,11 +234,7 @@ export class MainThreadDocuments extends MainThreadDocumentsShape { private _handleAsResourceInput(uri: URI): TPromise { return this._textModelResolverService.createModelReference(uri).then(ref => { - // TimeoutReference will check every 3 min if the - // reference is still in use. This is quite harsh to - // extensions but we don't want them to make us hold - // on to model indefinitely - this._toDispose.push(new TimeoutReference(this._codeEditorService, this._editorGroupService, ref)); + this._modelReferenceCollection.add(ref); const result = !!ref.object; return result; }); diff --git a/src/vs/workbench/api/node/mainThreadOutputService.ts b/src/vs/workbench/api/node/mainThreadOutputService.ts index b0a32a6202d..6694cc7cd8f 100644 --- a/src/vs/workbench/api/node/mainThreadOutputService.ts +++ b/src/vs/workbench/api/node/mainThreadOutputService.ts @@ -43,7 +43,7 @@ export class MainThreadOutputService extends MainThreadOutputServiceShape { } private _getChannel(channelId: string, label: string): IOutputChannel { - if (Registry.as(Extensions.OutputChannels).getChannels().every(channel => channel.id !== channelId)) { + if (!Registry.as(Extensions.OutputChannels).getChannel(channelId)) { Registry.as(Extensions.OutputChannels).registerChannel(channelId, label); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 9e22888aa24..b2b95a85d73 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -34,7 +34,7 @@ import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IFilesConfiguration, SUPPORTED_ENCODINGS, isEqual } from 'vs/platform/files/common/files'; +import { IFilesConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -649,7 +649,7 @@ export class EditorStatus implements IStatusbarItem { const activeEditor = this.editorService.getActiveEditor(); if (activeEditor) { const activeResource = toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }); - if (isEqual(activeResource, resource)) { + if (activeResource.toString() === resource.toString()) { return this.onEncodingChange(activeEditor); // only update if the encoding changed for the active resource } } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index ce054f4e74c..3afe46db746 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -26,7 +26,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import Event from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { highContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme'; +import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; export class SidebarPart extends CompositePart { @@ -81,6 +81,8 @@ export class SidebarPart extends CompositePart { // Part container const container = this.getContainer(); + container.style('background-color', this.getColor(SIDE_BAR_BACKGROUND)); + const useBorder = this.isHighContrastTheme; const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT; container.style('border-right-width', useBorder && isPositionLeft ? '1px' : null); diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 2b45a49546f..cf35c2c5f9a 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -15,7 +15,6 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/platform'; import { Position, Direction } from 'vs/platform/editor/common/editor'; -import { isEqual } from 'vs/platform/files/common/files'; import { ResourceMap } from 'vs/base/common/map'; export interface GroupEvent extends IGroupEvent { @@ -199,7 +198,7 @@ export class EditorGroup implements IEditorGroup { for (let i = 0; i < this.editors.length; i++) { const editor = this.editors[i]; const editorResource = toResource(editor, { supportSideBySide: true }); - if (isEqual(editorResource, resource)) { + if (editorResource.toString() === resource.toString()) { return editor; } } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index b059f256340..4056283c1cf 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -9,6 +9,16 @@ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { Color, RGBA } from 'vs/base/common/color'; +// < --- Workbench --- > + +export const WINDOW_FOREGROUND = registerColor('windowForeground', { + dark: '#CCCCCC', + light: '#6C6C6C', + hc: '#FFFFFF' +}, nls.localize('windowForeground', "Overall window foreground color. This color is only used if not overridden by a component.")); + + + // < --- Tabs --- > export const TABS_CONTAINER_BACKGROUND = registerColor('tabsContainerBackground', { @@ -162,6 +172,12 @@ export const ACTIVITY_BAR_BACKGROUND = registerColor('activityBarBackground', { // < --- Side Bar --- > +export const SIDE_BAR_BACKGROUND = registerColor('sideBarBackground', { + dark: '#252526', + light: '#F3F3F3', + hc: '#000000' +}, nls.localize('sideBarBackground', "Side bar background color. The side bar is the container for views like explorer and search.")); + export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitleForeground', { dark: '#BBBBBB', light: '#6f6f6f', @@ -176,25 +192,25 @@ export const TITLE_BAR_ACTIVE_FOREGROUND = registerColor('titleBarActiveForegrou dark: '#CCCCCC', light: '#333333', hc: '#FFFFFF' -}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active. Note that this color is currently only on supported on macOS.")); +}, nls.localize('titleBarActiveForeground', "Title bar foreground when the window is active. Note that this color is currently only supported on macOS.")); export const TITLE_BAR_INACTIVE_FOREGROUND = registerColor('titleBarInactiveForeground', { dark: Color.fromRGBA(new RGBA(204, 204, 204)).transparent(0.6), light: Color.fromRGBA(new RGBA(51, 51, 51)).transparent(0.6), hc: null -}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive. Note that this color is currently only on supported on macOS.")); +}, nls.localize('titleBarInactiveForeground', "Title bar foreground when the window is inactive. Note that this color is currently only supported on macOS.")); export const TITLE_BAR_ACTIVE_BACKGROUND = registerColor('titleBarActiveBackground', { dark: '#3C3C3C', light: '#DDDDDD', hc: '#000000' -}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active. Note that this color is currently only on supported on macOS.")); +}, nls.localize('titleBarActiveBackground', "Title bar background when the window is active. Note that this color is currently only supported on macOS.")); export const TITLE_BAR_INACTIVE_BACKGROUND = registerColor('titleBarInactiveBackground', { dark: Color.fromRGBA(new RGBA(60, 60, 60)).transparent(0.6), light: Color.fromRGBA(new RGBA(221, 221, 221)).transparent(0.6), hc: null -}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive. Note that this color is currently only on supported on macOS.")); +}, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive. Note that this color is currently only supported on macOS.")); /** * Base class for all themable workbench components. @@ -244,4 +260,4 @@ export class Themable extends Disposable { super.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index fa70c0937b5..9b675e33f8c 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -677,10 +677,21 @@ export class ReportIssueAction extends Action { super(id, label); } + private _optimisticIsPure(): TPromise { + let isPure = true; + let integrityPromise = this.integrityService.isPure().then(res => { + isPure = res.isPure; + }); + + return TPromise.any([TPromise.timeout(100), integrityPromise]).then(() => { + return isPure; + }); + } + public run(): TPromise { - return this.integrityService.isPure().then(res => { + return this._optimisticIsPure().then(isPure => { return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { - const issueUrl = this.generateNewIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, res.isPure, extensions); + const issueUrl = this.generateNewIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, isPure, extensions); window.open(issueUrl); diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index 160cdbf597b..0b99abcd27c 100644 --- a/src/vs/workbench/electron-browser/media/shell.css +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -204,21 +204,7 @@ /* END Keyboard Focus Indication Styles */ -/* TODO@theme */ - -.monaco-shell.vs { - color: #6C6C6C; -} - -.monaco-shell.vs-dark { - color: #BBB; - background-color: #1E1E1E; -} - -.monaco-shell.hc-black { - color: #fff; - background-color: #000; -} +/* TODO@theme (widgets) */ .monaco-shell.vs input { background-color: white; diff --git a/src/vs/workbench/electron-browser/media/workbench.css b/src/vs/workbench/electron-browser/media/workbench.css index fd1fa6a4ae7..f355796a2a7 100644 --- a/src/vs/workbench/electron-browser/media/workbench.css +++ b/src/vs/workbench/electron-browser/media/workbench.css @@ -37,39 +37,7 @@ z-index: 10000; } -.hc-black #monaco-workbench-drop-overlay { - background: none !important; - outline: 2px dashed; - outline-offset: -2px; -} - -/* TODO@theme */ - -.vs .monaco-workbench { - background-color: #F3F3F3; -} - -.vs-dark .monaco-workbench { - background-color: #252526; - color: #CCC; -} - -.hc-black .monaco-workbench { - color: #FFF; - background-color: #000; -} - -.vs #monaco-workbench-drop-overlay { - background-color: rgba(51,153,255, 0.18); -} - -.vs-dark #monaco-workbench-drop-overlay { - background-color: rgba(83, 89, 93, 0.5); -} - -.hc-black #monaco-workbench-drop-overlay { - outline-color: #f38518; -} +/* TODO@theme (widgets) */ .vs-dark .monaco-workbench .monaco-scrollable-element .shadow.top { box-shadow: #000 0 6px 6px -6px inset; diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 7e0684ca5b6..b523a7648bb 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -103,6 +103,8 @@ import { join } from 'path'; import 'vs/platform/opener/browser/opener.contribution'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/themeService'; import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/themeService'; +import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +import { WINDOW_FOREGROUND } from 'vs/workbench/common/theme'; /** * Services that we require for the Shell @@ -509,3 +511,10 @@ export class WorkbenchShell { $(this.container).empty(); } } + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const windowForeground = theme.getColor(WINDOW_FOREGROUND); + if (windowForeground) { + collector.addRule(`.monaco-shell { color: ${windowForeground}; }`); + } +}); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index aa9e404e4aa..dededc68b61 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -47,8 +47,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { Position, IResourceInput } from 'vs/platform/editor/common/editor'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { Themable, EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { remote, ipcRenderer as ipc, webFrame } from 'electron'; +import { highContrastOutline } from 'vs/platform/theme/common/colorRegistry'; const dialog = remote.dialog; @@ -63,7 +65,7 @@ const TextInputActions: IAction[] = [ new Action('editor.action.selectAll', nls.localize('selectAll', "Select All"), null, true, () => document.execCommand('selectAll') && TPromise.as(true)) ]; -export class ElectronWindow { +export class ElectronWindow extends Themable { private static AUTO_SAVE_SETTING = 'files.autoSave'; @@ -81,7 +83,7 @@ export class ElectronWindow { @IWindowService private windowService: IWindowService, @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, @ITitleService private titleService: ITitleService, - @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @IMessageService private messageService: IMessageService, @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @ICommandService private commandService: ICommandService, @@ -92,6 +94,8 @@ export class ElectronWindow { @IEnvironmentService private environmentService: IEnvironmentService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, ) { + super(themeService); + this.win = win; this.windowId = win.id; @@ -132,8 +136,18 @@ export class ElectronWindow { // Find out if folders are dragged and show the appropiate feedback then this.includesFolder(draggedExternalResources).done(includesFolder => { if (includesFolder) { + const useOutline = this.isHighContrastTheme; dropOverlay = $(window.document.getElementById(this.partService.getWorkbenchElementId())) - .div({ id: 'monaco-workbench-drop-overlay' }) + .div({ + id: 'monaco-workbench-drop-overlay' + }) + .style({ + backgroundColor: this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND), + outlineColor: useOutline ? this.getColor(highContrastOutline) : null, + outlineOffset: useOutline ? '-2px' : null, + outlineStyle: useOutline ? 'dashed' : null, + outlineWidth: useOutline ? '2px' : null + }) .on(DOM.EventType.DROP, (e: DragEvent) => { DOM.EventHelper.stop(e, true); diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index 52a691104fb..4ee6bb14480 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as path from 'path'; -import * as os from 'os'; import * as cp from 'child_process'; import * as pfs from 'vs/base/node/pfs'; import { nfcall } from 'vs/base/common/async'; @@ -13,12 +12,10 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry'; -import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/platform'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IEditorService } from 'vs/platform/editor/common/editor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import product from 'vs/platform/node/product'; interface ILegacyUse { @@ -30,10 +27,6 @@ function ignore(code: string, value: T = null): (err: any) => TPromise { return err => err.code === code ? TPromise.as(value) : TPromise.wrapError(err); } -function readOrEmpty(name: string): TPromise { - return pfs.readFile(name, 'utf8').then(null, ignore('ENOENT', '')); -} - const root = URI.parse(require.toUrl('')).fsPath; const source = path.resolve(root, '..', 'bin', 'code'); @@ -67,61 +60,28 @@ class InstallAction extends Action { return undefined; } - return this.checkLegacy() - .then(uses => { - if (uses.length > 0) { - const { file, lineNumber } = uses[0]; - const message = nls.localize( - 'exists', - "Please remove the alias referencing '{0}' in '{1}' (line {2}) and retry this action.", - product.darwinBundleIdentifier, - file, - lineNumber - ); - - const resource = URI.file(file); - const input = { resource }; - const actions = [ - new Action('inlineEdit', nls.localize('editFile', "Edit '{0}'", file), '', true, () => { - return this.editorService.openEditor(input).then(() => { - const message = nls.localize('again', "Please remove the '{0}' alias from '{1}' before continuing.", product.applicationName, file); - const actions = [ - new Action('continue', nls.localize('continue', "Continue"), '', true, () => this.run()), - new Action('cancel', nls.localize('cancel', "Cancel")) - ]; - - this.messageService.show(Severity.Info, { message, actions }); - }); - }) - ]; - - this.messageService.show(Severity.Warning, { message, actions }); + return this.isInstalled() + .then(isInstalled => { + if (!isAvailable || isInstalled) { return TPromise.as(null); - } + } else { + const createSymlink = () => { + return pfs.unlink(this.target) + .then(null, ignore('ENOENT')) + .then(() => pfs.symlink(source, this.target)); + }; - return this.isInstalled() - .then(isInstalled => { - if (!isAvailable || isInstalled) { - return TPromise.as(null); - } else { - const createSymlink = () => { - return pfs.unlink(this.target) - .then(null, ignore('ENOENT')) - .then(() => pfs.symlink(source, this.target)); - }; - - return createSymlink().then(null, err => { - if (err.code === 'EACCES' || err.code === 'ENOENT') { - return this.createBinFolder() - .then(() => createSymlink()); - } - - return TPromise.wrapError(err); - }); + return createSymlink().then(null, err => { + if (err.code === 'EACCES' || err.code === 'ENOENT') { + return this.createBinFolder() + .then(() => createSymlink()); } - }) - .then(() => this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName))); - }); + + return TPromise.wrapError(err); + }); + } + }) + .then(() => this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName))); }); } @@ -152,32 +112,6 @@ class InstallAction extends Action { this.messageService.show(Severity.Info, { message, actions }); }); } - - checkLegacy(): TPromise { - const files = [ - path.join(os.homedir(), '.bash_profile'), - path.join(os.homedir(), '.bashrc'), - path.join(os.homedir(), '.zshrc') - ]; - - return TPromise.join(files.map(f => readOrEmpty(f))).then(result => { - return result.reduce((result, contents, index) => { - const file = files[index]; - const lines = contents.split(/\r?\n/); - - lines.some((line, index) => { - if (line.indexOf(product.darwinBundleIdentifier) > -1 && !/^\s*#/.test(line)) { - result.push({ file, lineNumber: index + 1 }); - return true; - } - - return false; - }); - - return result; - }, [] as ILegacyUse[]); - }); - } } class UninstallAction extends Action { @@ -212,47 +146,10 @@ class UninstallAction extends Action { } } -class DarwinCLIHelper implements IWorkbenchContribution { - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IMessageService messageService: IMessageService - ) { - const installAction = instantiationService.createInstance(InstallAction, InstallAction.ID, InstallAction.LABEL); - - isAvailable().done(isAvailable => { - if (!isAvailable) { - return; - } - - return installAction.checkLegacy().done(files => { - if (files.length > 0) { - const message = nls.localize('update', "Code needs to change the '{0}' shell command. Would you like to do this now?", product.applicationName); - const now = new Action('changeNow', nls.localize('changeNow', "Change Now"), '', true, () => installAction.run()); - const later = new Action('later', nls.localize('later', "Later"), '', true, () => { - messageService.show(Severity.Info, nls.localize('laterInfo', "Remember you can always run the '{0}' action from the Command Palette.", installAction.label)); - return null; - }); - const actions = [now, later]; - - messageService.show(Severity.Info, { message, actions }); - } - }); - }); - } - - getId(): string { - return 'darwin.cli'; - } -} - if (process.platform === 'darwin') { const category = nls.localize('shellCommand', "Shell Command"); const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallAction, InstallAction.ID, InstallAction.LABEL), 'Shell Command: Install \'code\' command in PATH', category); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(UninstallAction, UninstallAction.ID, UninstallAction.LABEL), 'Shell Command: Uninstall \'code\' command from PATH', category); - - const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); - workbenchRegistry.registerWorkbenchContribution(DarwinCLIHelper); } diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 085f2a7e38f..abf5c196b48 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -208,7 +208,7 @@ export class DebugActionsWidget implements IWorkbenchContribution { const state = this.debugService.state; const process = this.debugService.getViewModel().focusedProcess; - const attached = process && process.configuration.request && strings.equalsIgnoreCase(process.configuration.request, 'attach') && !strings.equalsIgnoreCase(process.configuration.type, 'extensionHost'); + const attached = process && process.configuration.request === 'attach' && process.configuration.type && !strings.equalsIgnoreCase(process.configuration.type, 'extensionHost'); return this.allActions.filter(a => { if (a.id === ContinueAction.ID) { diff --git a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts index b2143f8f7fa..f53978b97b3 100644 --- a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts @@ -9,13 +9,13 @@ import * as dom from 'vs/base/browser/dom'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IDebugService } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug'; import { RunOnceScheduler } from 'vs/base/common/async'; const $ = dom.$; export class ExceptionWidget extends ZoneWidget { - constructor(editor: ICodeEditor, private lineNumber: number, + constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private lineNumber: number, @IContextViewService private contextViewService: IContextViewService, @IDebugService private debugService: IDebugService ) { @@ -35,15 +35,38 @@ export class ExceptionWidget extends ZoneWidget { this.container.style.lineHeight = `${fontInfo.lineHeight}px`; let title = $('.title'); - title.textContent = nls.localize('exceptionThrown', 'Exception occurred'); - dom.append(container, title); + let msg = $('.message'); + const defaultConditionMessage = nls.localize('exceptionThrown', 'Exception has occurred.'); - const thread = this.debugService.getViewModel().focusedThread; - if (thread && thread.stoppedDetails) { - let msg = $('.message'); - msg.textContent = thread.stoppedDetails.text; - dom.append(container, msg); + if (this.exceptionInfo.breakMode) { + let conditionMessage; + switch (this.exceptionInfo.breakMode) { + case 'never': + conditionMessage = nls.localize('neverException', 'User-handled exception has occurred.'); + break; + case 'always': + conditionMessage = nls.localize('alwaysException', 'Always-breaking exception has occurred.'); + break; + case 'unhandled': + conditionMessage = nls.localize('unhandledException', 'Unhandled exception has occurred.'); + break; + case 'userUnhandled': + conditionMessage = nls.localize('userUnhandledException', 'User-unhandled exception has occurred.'); + break; + default: + conditionMessage = defaultConditionMessage; + break; + } + + title.textContent = `${conditionMessage} ${this.exceptionInfo.description}`; + msg.textContent = this.exceptionInfo.details.stackTrace; + } else { + title.textContent = defaultConditionMessage; + msg.textContent = this.exceptionInfo.description; } + + dom.append(container, title); + dom.append(container, msg); } protected _doLayout(heightInPixel: number, widthInPixel: number): void { diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 89c302e1e52..a57deed571f 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -77,6 +77,7 @@ export interface IExpression extends ITreeElement, IExpressionContainer { export interface ISession { stackTrace(args: DebugProtocol.StackTraceArguments): TPromise; + exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise; scopes(args: DebugProtocol.ScopesArguments): TPromise; variables(args: DebugProtocol.VariablesArguments): TPromise; evaluate(args: DebugProtocol.EvaluateArguments): TPromise; @@ -133,6 +134,11 @@ export interface IThread extends ITreeElement { */ stoppedDetails: IRawStoppedDetails; + /** + * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise null. + */ + exceptionInfo: TPromise; + /** * Gets the callstack if it has already been received from the debug * adapter, otherwise it returns null. @@ -214,6 +220,13 @@ export interface IExceptionBreakpoint extends IEnablement { label: string; } +export interface IExceptionInfo { + id?: string; + description?: string; + breakMode: string; + details?: DebugProtocol.ExceptionDetails; +} + // model interfaces export interface IViewModel extends ITreeElement { diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index cdbbf34d85e..e4c7a181232 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -19,7 +19,7 @@ import { ISuggestion } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import { ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, - IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint + IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -490,6 +490,30 @@ export class Thread implements IThread { }); } + /** + * Returns exception info promise if the exception was thrown, otherwise null + */ + public get exceptionInfo(): TPromise { + const session = this.process.session; + if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') { + if (!session.capabilities.supportsExceptionInfoRequest) { + return TPromise.as({ + description: this.stoppedDetails.text, + breakMode: null + }); + } + + return session.exceptionInfo({ threadId: this.threadId }).then(exception => ({ + id: exception.body.exceptionId, + description: exception.body.description, + breakMode: exception.body.breakMode, + details: exception.body.details + })); + } + + return TPromise.as(null); + } + public next(): TPromise { return this.process.session.next({ threadId: this.threadId }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index e641ac792ee..80578f070e4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -30,7 +30,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; import { DebugHoverWidget } from 'vs/workbench/parts/debug/electron-browser/debugHover'; import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpointAction, DisableBreakpointAction, AddConditionalBreakpointAction } from 'vs/workbench/parts/debug/browser/debugActions'; -import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug'; import { BreakpointWidget } from 'vs/workbench/parts/debug/browser/breakpointWidget'; import { ExceptionWidget } from 'vs/workbench/parts/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; @@ -360,17 +360,21 @@ export class DebugEditorContribution implements IDebugEditorContribution { const sameUri = exceptionSf.source.uri.toString() === model.uri.toString(); if (this.exceptionWidget && !sameUri) { this.closeExceptionWidget(); - } else if (sameUri && focusedSf.thread.stoppedDetails && focusedSf.thread.stoppedDetails.reason === 'exception') { - this.showExceptionWidget(exceptionSf.lineNumber, exceptionSf.column); + } else if (sameUri) { + focusedSf.thread.exceptionInfo.then(exceptionInfo => { + if (exceptionInfo) { + this.showExceptionWidget(exceptionInfo, exceptionSf.lineNumber, exceptionSf.column); + } + }); } } - private showExceptionWidget(lineNumber: number, column: number): void { + private showExceptionWidget(exceptionInfo: IExceptionInfo, lineNumber: number, column: number): void { if (this.exceptionWidget) { this.exceptionWidget.dispose(); } - this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, lineNumber); + this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, lineNumber); this.exceptionWidget.show({ lineNumber, column }, 0); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 6b0780d09cc..5cc75ec3d50 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -871,7 +871,8 @@ export class DebugService implements debug.IDebugService { if (process.session.capabilities.supportsRestartRequest) { return process.session.custom('restart', null); } - const preserveFocus = process.getId() === this.viewModel.focusedProcess.getId(); + const focusedProcess = this.viewModel.focusedProcess; + const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId(); return process.session.disconnect(true).then(() => new TPromise((c, e) => { diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 473b272669a..620214150d0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -323,6 +323,10 @@ export class RawDebugSession extends v8.V8Protocol implements debug.ISession { return this.send('stackTrace', args); } + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + return this.send('exceptionInfo', args); + } + public scopes(args: DebugProtocol.ScopesArguments): TPromise { return this.send('scopes', args); } diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index 39a7135dee4..4f401f39687 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -120,6 +120,15 @@ export class MockSession implements debug.ISession { }); } + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + return TPromise.as({ + body: { + exceptionId: 'mockExceptionId', + breakMode: 'unhandled' + } + }); + } + public attach(args: DebugProtocol.AttachRequestArguments): TPromise { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index aa60df1b44a..ee5e974e811 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -326,7 +326,12 @@ export class ExtensionEditor extends BaseEditor { webview.style(this.themeService.getColorTheme()); webview.contents = [body]; - webview.onDidClickLink(link => this.openerService.open(link), null, this.contentDisposables); + webview.onDidClickLink(link => { + // Whitelist supported schemes for links + if (link && ['http', 'https', 'mailto'].indexOf(link.scheme) >= 0) { + this.openerService.open(link); + } + }, null, this.contentDisposables); this.themeService.onDidColorThemeChange(theme => webview.style(theme), null, this.contentDisposables); this.contentDisposables.push(webview); }) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index ae2d55b0879..1c2d86fb147 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -228,42 +228,51 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { .done(null, err => this.onError(err)); } - private doSearch(value: string = '', suggestPopular = false): TPromise { - return this.progress(this.query(value)) - .then(model => { - if (!value && model.length === 0 && suggestPopular) { - return this.search('@sort:installs '); - } + private async doSearch(value: string = '', suggestPopular = false): TPromise { + const model = await this.progress(this.query(value)); - this.setModel(model); - }); + if (!value && model.length === 0 && suggestPopular) { + return this.search('@sort:installs '); + } + + this.setModel(model); } - private query(value: string): TPromise> { + private async query(value: string): TPromise> { if (!value || /@installed/i.test(value)) { // Show installed extensions value = value ? value.replace(/@installed/g, '').trim().toLowerCase() : ''; - return this.extensionsWorkbenchService.queryLocal() - .then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))) - .then(result => result.filter(e => e.type === LocalExtensionType.User && e.name.toLowerCase().indexOf(value) > -1)) - .then(result => new PagedModel(result)); + + const local = await this.extensionsWorkbenchService.queryLocal(); + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(e => e.type === LocalExtensionType.User && e.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); } if (/@outdated/i.test(value)) { value = value.replace(/@outdated/g, '').trim().toLowerCase(); - return this.extensionsWorkbenchService.queryLocal() - .then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))) - .then(extensions => extensions.filter(extension => extension.outdated && extension.name.toLowerCase().indexOf(value) > -1)) - .then(result => new PagedModel(result)); + + const local = await this.extensionsWorkbenchService.queryLocal(); + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(extension => extension.outdated && extension.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); } if (/@disabled/i.test(value)) { value = value.replace(/@disabled/g, '').trim().toLowerCase(); - return this.extensionsWorkbenchService.queryLocal() - .then(result => result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))) - .then(result => this.extensionService.getExtensions() - .then(runningExtensions => result.filter(e => runningExtensions.every(r => !areSameExtensions(r, e)) && e.name.toLowerCase().indexOf(value) > -1))) - .then(result => new PagedModel(result)); + + const local = await this.extensionsWorkbenchService.queryLocal(); + const runningExtensions = await this.extensionService.getExtensions(); + + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(e => runningExtensions.every(r => !areSameExtensions(r, e)) && e.name.toLowerCase().indexOf(value) > -1); + + return new PagedModel(result); } const query = Query.parse(value); @@ -287,7 +296,7 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { return this.getRecommendationsModel(query, options); } - const pagers: TPromise>[] = []; + const pagerPromises: TPromise>[] = []; let text = query.value; const extensionRegex = /\bext:([^\s]+)\b/g; @@ -311,7 +320,7 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { if (names.length) { const namesOptions = assign({}, options, { names }); - pagers.push(this.extensionsWorkbenchService.queryGallery(namesOptions)); + pagerPromises.push(this.extensionsWorkbenchService.queryGallery(namesOptions)); } } @@ -319,12 +328,12 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet { options = assign(options, { text: text.substr(0, 350) }); } - pagers.push(this.extensionsWorkbenchService.queryGallery(options)); + pagerPromises.push(this.extensionsWorkbenchService.queryGallery(options)); - return TPromise.join(pagers).then(pagers => { - const pager = pagers.length === 2 ? mergePagers(pagers[0], pagers[1]) : pagers[0]; - return new PagedModel(pager); - }); + const pagers = await TPromise.join(pagerPromises); + const pager = pagers.length === 2 ? mergePagers(pagers[0], pagers[1]) : pagers[0]; + + return new PagedModel(pager); } private getRecommendationsModel(query: Query, options: IQueryOptions): TPromise> { diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 8e47c173486..b6eb5184b5f 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -288,7 +288,7 @@ class RenameFileAction extends BaseRenameAction { public runAction(newName: string): TPromise { let isDirtyCaseChange = false; - const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath)); + const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); const dirtyRenamed = dirty.map(d => { const targetPath = paths.join(this.element.parent.resource.fsPath, newName); let renamed: URI; @@ -702,7 +702,7 @@ export class BaseDeleteFileAction extends BaseFileAction { // Handle dirty let revertPromise: TPromise = TPromise.as(null); - const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath)); + const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); if (dirty.length) { let message: string; if (this.element.isDirectory) { @@ -994,7 +994,7 @@ export class PasteFileAction extends BaseFileAction { } // Check if target is ancestor of pasted folder - if (!isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && isEqualOrParent(this.element.resource.fsPath, fileToCopy.resource.fsPath)) { + if (!isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && isEqualOrParent(this.element.resource.fsPath, fileToCopy.resource.fsPath, !isLinux /* ignorecase */)) { return false; } @@ -1099,8 +1099,8 @@ export class DuplicateFileAction extends BaseFileAction { private toCopyName(name: string, isFolder: boolean): string { // file.1.txt=>file.2.txt - if (!isFolder && name.match(/(\d+)(\..*)$/)) { - return name.replace(/(\d+)(\..*)$/, (match, g1?, g2?) => { return (parseInt(g1) + 1) + g2; }); + if (!isFolder && name.match(/(.*\.)(\d+)(\..*)$/)) { + return name.replace(/(.*\.)(\d+)(\..*)$/, (match, g1?, g2?, g3?) => { return g1 + (parseInt(g2) + 1) + g3; }); } // file.txt=>file.1.txt diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index dc2cbcaa081..457e1a4c214 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -41,6 +41,7 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ResourceContextKey } from 'vs/workbench/common/resourceContextKey'; import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/themeService'; +import { isLinux } from 'vs/base/common/platform'; export class ExplorerView extends CollapsibleViewletView { @@ -734,7 +735,7 @@ export class ExplorerView extends CollapsibleViewletView { // Drop those path which are parents of the current one for (let i = resolvedDirectories.length - 1; i >= 0; i--) { const resource = resolvedDirectories[i]; - if (isEqualOrParent(stat.resource.fsPath, resource.fsPath)) { + if (isEqualOrParent(stat.resource.fsPath, resource.fsPath, !isLinux /* ignorecase */)) { resolvedDirectories.splice(i); } } diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index a7ef92c1023..af9ef658fcd 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -19,7 +19,7 @@ import { IAction, ActionRunner as BaseActionRunner, IActionRunner } from 'vs/bas import comparers = require('vs/base/common/comparers'); import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { $, Builder } from 'vs/base/browser/builder'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; import glob = require('vs/base/common/glob'); import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -697,7 +697,7 @@ export class FileDragAndDrop implements IDragAndDrop { return true; // Can not move a file to the same parent unless we copy } - if (isEqualOrParent(target.resource.fsPath, source.resource.fsPath)) { + if (isEqualOrParent(target.resource.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)) { return true; // Can not move a parent folder into one of its children } @@ -759,7 +759,7 @@ export class FileDragAndDrop implements IDragAndDrop { }; // 1. check for dirty files that are being moved and backup to new target - const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, source.resource.fsPath)); + const dirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)); return TPromise.join(dirty.map(d => { let moved: URI; @@ -802,7 +802,7 @@ export class FileDragAndDrop implements IDragAndDrop { // Move with overwrite if the user confirms if (this.messageService.confirm(confirm)) { - const targetDirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, targetResource.fsPath)); + const targetDirty = this.textFileService.getDirty().filter(d => isEqualOrParent(d.fsPath, targetResource.fsPath, !isLinux /* ignorecase */)); // Make sure to revert all dirty in target first to be able to overwrite properly return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => { diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts index 30038c6973d..1ea7c0e7452 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -22,6 +22,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { distinct } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { isLinux } from 'vs/base/common/platform'; export class FileEditorTracker implements IWorkbenchContribution { private stacks: IEditorStacksModel; @@ -114,7 +115,7 @@ export class FileEditorTracker implements IWorkbenchContribution { // Do NOT close any opened editor that matches the resource path (either equal or being parent) of the // resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same // path but different casing. - if (movedTo && isEqualOrParent(resource.fsPath, movedTo.fsPath) && resource.fsPath.indexOf(movedTo.fsPath) === 0) { + if (movedTo && isEqualOrParent(resource.fsPath, movedTo.fsPath, !isLinux /* ignorecase */) && resource.fsPath.indexOf(movedTo.fsPath) === 0) { return; } @@ -122,7 +123,7 @@ export class FileEditorTracker implements IWorkbenchContribution { if (arg1 instanceof FileChangesEvent) { matches = arg1.contains(resource, FileChangeType.DELETED); } else { - matches = isEqualOrParent(resource.fsPath, arg1.fsPath); + matches = isEqualOrParent(resource.fsPath, arg1.fsPath, !isLinux /* ignorecase */); } if (!matches) { @@ -194,12 +195,12 @@ export class FileEditorTracker implements IWorkbenchContribution { const resource = input.getResource(); // Update Editor if file (or any parent of the input) got renamed or moved - if (isEqualOrParent(resource.fsPath, oldResource.fsPath)) { + if (isEqualOrParent(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */)) { let reopenFileResource: URI; if (oldResource.toString() === resource.toString()) { reopenFileResource = newResource; // file got moved } else { - const index = indexOf(resource.fsPath, oldResource.fsPath); + const index = indexOf(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */); reopenFileResource = URI.file(paths.join(newResource.fsPath, resource.fsPath.substr(index + oldResource.fsPath.length + 1))); // parent folder got moved } diff --git a/src/vs/workbench/parts/files/common/explorerViewModel.ts b/src/vs/workbench/parts/files/common/explorerViewModel.ts index 33066d5f6e6..df308c44db1 100644 --- a/src/vs/workbench/parts/files/common/explorerViewModel.ts +++ b/src/vs/workbench/parts/files/common/explorerViewModel.ts @@ -12,6 +12,7 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn import { IEditorInput } from 'vs/platform/editor/common/editor'; import { IEditorGroup, toResource } from 'vs/workbench/common/editor'; import { ResourceMap } from 'vs/base/common/map'; +import { isLinux } from 'vs/base/common/platform'; export enum StatType { FILE, @@ -61,7 +62,7 @@ export class FileStat implements IFileStat { // the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo // array of resource path to resolve. stat.isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => { - return isEqualOrParent(r.fsPath, stat.resource.fsPath); + return isEqualOrParent(r.fsPath, stat.resource.fsPath, !isLinux /* ignorecase */); })); // Recurse into children @@ -241,7 +242,7 @@ export class FileStat implements IFileStat { public find(resource: URI): FileStat { // Return if path found - if (isEqual(resource.fsPath, this.resource.fsPath)) { + if (isEqual(resource.fsPath, this.resource.fsPath, !isLinux /* ignorecase */)) { return this; } @@ -253,11 +254,11 @@ export class FileStat implements IFileStat { for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; - if (isEqual(resource.fsPath, child.resource.fsPath)) { + if (isEqual(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) { return child; } - if (child.isDirectory && isParent(resource.fsPath, child.resource.fsPath)) { + if (child.isDirectory && isParent(resource.fsPath, child.resource.fsPath, !isLinux /* ignorecase */)) { return child.find(resource); } } diff --git a/src/vs/workbench/parts/files/test/browser/explorerViewModel.test.ts b/src/vs/workbench/parts/files/test/browser/explorerViewModel.test.ts index 92b2fdd10a3..9258f4bf6f2 100644 --- a/src/vs/workbench/parts/files/test/browser/explorerViewModel.test.ts +++ b/src/vs/workbench/parts/files/test/browser/explorerViewModel.test.ts @@ -206,14 +206,16 @@ suite('Files - View Model', () => { assert(validateFileName(s, ' ') !== null); assert(validateFileName(s, 'Read Me') === null, 'name containing space'); assert(validateFileName(s, 'foo/bar') !== null); - assert(validateFileName(s, 'foo\\bar') !== null); if (isWindows) { + assert(validateFileName(s, 'foo\\bar') !== null); assert(validateFileName(s, 'foo:bar') !== null); assert(validateFileName(s, 'foo*bar') !== null); assert(validateFileName(s, 'foo?bar') !== null); assert(validateFileName(s, 'foobar') !== null); assert(validateFileName(s, 'foo|bar') !== null); + } else { + assert(validateFileName(s, 'foo\\bar') === null); } assert(validateFileName(s, 'alles.klar') !== null); diff --git a/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts index c878e02bb98..e4e42670c14 100644 --- a/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts @@ -16,7 +16,6 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { FileOperationResult, IFileOperationResult } from 'vs/platform/files/common/files'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { Verbosity } from 'vs/platform/editor/common/editor'; -import { isLinux } from 'vs/base/common/platform'; function toResource(path) { return URI.file(join('C:\\', new Buffer(this.test.fullTitle()).toString('base64'), path)); @@ -115,11 +114,7 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(input1.matches(input2), true); assert.strictEqual(input1.matches(input3), false); - if (isLinux) { - assert.strictEqual(input1.matches(input2Upper), false); - } else { - assert.strictEqual(input1.matches(input2Upper), true); - } + assert.strictEqual(input1.matches(input2Upper), false); }); test('getEncoding/setEncoding', function (done) { diff --git a/src/vs/workbench/parts/git/browser/views/changes/changesView.ts b/src/vs/workbench/parts/git/browser/views/changes/changesView.ts index 7be73c477ce..48f3037f246 100644 --- a/src/vs/workbench/parts/git/browser/views/changes/changesView.ts +++ b/src/vs/workbench/parts/git/browser/views/changes/changesView.ts @@ -453,12 +453,12 @@ export class ChangesView extends EventEmitter.EventEmitter implements GitView.IV const resource = WorkbenchEditorCommon.toResource(input, { filter: 'file' }); if (resource) { const workspaceRoot = this.contextService.getWorkspace().resource.fsPath; - if (!workspaceRoot || !isEqualOrParent(resource.fsPath, workspaceRoot)) { + if (!workspaceRoot || !isEqualOrParent(resource.fsPath, workspaceRoot, !Platform.isLinux /* ignorecase */)) { return null; // out of workspace not yet supported } const repositoryRoot = this.gitService.getModel().getRepositoryRoot(); - if (!repositoryRoot || !isEqualOrParent(resource.fsPath, repositoryRoot)) { + if (!repositoryRoot || !isEqualOrParent(resource.fsPath, repositoryRoot, !Platform.isLinux /* ignorecase */)) { return null; // out of repository not supported } diff --git a/src/vs/workbench/parts/markers/browser/markersPanel.ts b/src/vs/workbench/parts/markers/browser/markersPanel.ts index eae0551781f..ba408f9a445 100644 --- a/src/vs/workbench/parts/markers/browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/browser/markersPanel.ts @@ -247,7 +247,7 @@ export class MarkersPanel extends Panel { if (resourceForCurrentActiveFile) { return false; } - return changedResources.some(r => isEqual(r, this.currentActiveFile)); + return changedResources.some(r => r.toString() === this.currentActiveFile.toString()); } private onEditorsChanged(): void { @@ -334,7 +334,7 @@ export class MarkersPanel extends Panel { private getResourceForCurrentActiveFile(): Resource { if (this.currentActiveFile) { let resources = this.markersModel.filteredResources.filter((resource): boolean => { - return isEqual(this.currentActiveFile, resource.uri); + return this.currentActiveFile.toString() === resource.uri.toString(); }); return resources.length > 0 ? resources[0] : null; } diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 5a0f7622cf3..ef225a8f426 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -107,9 +107,10 @@ export class SwitchOutputActionItem extends SelectActionItem { action: IAction, @IOutputService private outputService: IOutputService ) { - super(null, action, SwitchOutputActionItem.getChannelLabels(outputService), Math.max(0, SwitchOutputActionItem.getChannelLabels(outputService).indexOf(outputService.getActiveChannel().label))); - this.toDispose.push(this.outputService.onOutputChannel(this.onOutputChannel, this)); - this.toDispose.push(this.outputService.onActiveOutputChannel(this.onOutputChannel, this)); + super(null, action, [], 0); + this.toDispose.push(this.outputService.onOutputChannel(() => this.setOptions(this.getOptions(), this.getSelected(undefined)))); + this.toDispose.push(this.outputService.onActiveOutputChannel(activeChannelId => this.setOptions(this.getOptions(), this.getSelected(activeChannelId)))); + this.setOptions(this.getOptions(), this.getSelected(this.outputService.getActiveChannel().id)); } protected getActionContext(option: string): string { @@ -118,16 +119,15 @@ export class SwitchOutputActionItem extends SelectActionItem { return channel ? channel.id : option; } - private onOutputChannel(): void { - let channels = SwitchOutputActionItem.getChannelLabels(this.outputService); - let selected = Math.max(0, channels.indexOf(this.outputService.getActiveChannel().label)); - - this.setOptions(channels, selected); + private getOptions(): string[] { + return this.outputService.getChannels().map(c => c.label); } - private static getChannelLabels(outputService: IOutputService): string[] { - const contributedChannels = outputService.getChannels().map(channelData => channelData.label); + private getSelected(outputId: string): number { + if (!outputId) { + return undefined; + } - return contributedChannels.sort(); // sort by name + return Math.max(0, this.outputService.getChannels().map(c => c.id).indexOf(outputId)); } } diff --git a/src/vs/workbench/parts/output/browser/outputServices.ts b/src/vs/workbench/parts/output/browser/outputServices.ts index 4a584ff2f99..05e5dbefec0 100644 --- a/src/vs/workbench/parts/output/browser/outputServices.ts +++ b/src/vs/workbench/parts/output/browser/outputServices.ts @@ -6,6 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import strings = require('vs/base/common/strings'); import Event, { Emitter } from 'vs/base/common/event'; +import { binarySearch } from 'vs/base/common/arrays'; import URI from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEditor } from 'vs/platform/editor/common/editor'; @@ -13,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputChannelIdentifier, OutputEditors, IOutputEvent, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH, OUTPUT_SCHEME, OUTPUT_MIME } from 'vs/workbench/parts/output/common/output'; +import { IOutputChannelIdentifier, OutputEditors, IOutputEvent, IOutputChannel, IOutputService, IOutputDelta, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH, OUTPUT_SCHEME, OUTPUT_MIME } from 'vs/workbench/parts/output/common/output'; import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -28,11 +29,61 @@ import { Position } from 'vs/editor/common/core/position'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; +export class BufferedContent { + + private data: string[] = []; + private dataIds: number[] = []; + private idPool = 0; + private length = 0; + + public append(content: string): void { + this.data.push(content); + this.dataIds.push(++this.idPool); + this.length += content.length; + this.trim(); + } + + public clear(): void { + this.data.length = 0; + this.dataIds.length = 0; + this.length = 0; + } + + private trim(): void { + if (this.length < MAX_OUTPUT_LENGTH * 1.2) { + return; + } + + while (this.length > MAX_OUTPUT_LENGTH) { + this.dataIds.shift(); + const removed = this.data.shift(); + this.length -= removed.length; + } + } + + public getDelta(previousDelta?: IOutputDelta): IOutputDelta { + let idx = -1; + if (previousDelta) { + idx = binarySearch(this.dataIds, previousDelta.id, (a, b) => a - b); + } + + const id = this.idPool; + if (idx >= 0) { + const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join('')); + return { value, id, append: true }; + } else { + const value = strings.removeAnsiEscapeCodes(this.data.join('')); + return { value, id }; + } + } +} + export class OutputService implements IOutputService { public _serviceBrand: any; - private receivedOutput: Map; + private receivedOutput: Map = new Map(); + private channels: Map = new Map(); private activeChannelId: string; @@ -56,8 +107,6 @@ export class OutputService implements IOutputService { this._onOutputChannel = new Emitter(); this._onActiveOutputChannel = new Emitter(); - this.receivedOutput = new Map(); - const channels = this.getChannels(); this.activeChannelId = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, channels && channels.length > 0 ? channels[0].id : null); @@ -82,26 +131,30 @@ export class OutputService implements IOutputService { } public getChannel(id: string): IOutputChannel { - const channelData = this.getChannels().filter(channelData => channelData.id === id).pop(); + if (!this.channels.has(id)) { + const channelData = Registry.as(Extensions.OutputChannels).getChannel(id); - const self = this; - return { - id, - label: channelData ? channelData.label : id, - get output() { - return self.getOutput(id); - }, - get scrollLock() { - return self._outputContentProvider.scrollLock(id); - }, - set scrollLock(value: boolean) { - self._outputContentProvider.setScrollLock(id, value); - }, - append: (output: string) => this.append(id, output), - show: (preserveFocus: boolean) => this.showOutput(id, preserveFocus), - clear: () => this.clearOutput(id), - dispose: () => this.removeOutput(id) - }; + const self = this; + this.channels.set(id, { + id, + label: channelData ? channelData.label : id, + getOutput(before?: IOutputDelta) { + return self.getOutput(id, before); + }, + get scrollLock() { + return self._outputContentProvider.scrollLock(id); + }, + set scrollLock(value: boolean) { + self._outputContentProvider.setScrollLock(id, value); + }, + append: (output: string) => this.append(id, output), + show: (preserveFocus: boolean) => this.showOutput(id, preserveFocus), + clear: () => this.clearOutput(id), + dispose: () => this.removeOutput(id) + }); + } + + return this.channels.get(id); } public getChannels(): IOutputChannelIdentifier[] { @@ -112,34 +165,37 @@ export class OutputService implements IOutputService { // Initialize if (!this.receivedOutput.has(channelId)) { - this.receivedOutput.set(channelId, ''); + this.receivedOutput.set(channelId, new BufferedContent()); this._onOutputChannel.fire(channelId); // emit event that we have a new channel } - // Sanitize - output = strings.removeAnsiEscapeCodes(output); - // Store if (output) { - this.receivedOutput.set(channelId, strings.appendWithLimit(this.receivedOutput.get(channelId), output, MAX_OUTPUT_LENGTH)); + const channel = this.receivedOutput.get(channelId); + channel.append(output); } - this._onOutput.fire({ output: output, channelId: channelId }); + this._onOutput.fire({ channelId: channelId, isClear: false }); } public getActiveChannel(): IOutputChannel { return this.getChannel(this.activeChannelId); } - private getOutput(channelId: string): string { - return this.receivedOutput.get(channelId) || ''; + private getOutput(channelId: string, previousDelta: IOutputDelta): IOutputDelta { + if (this.receivedOutput.has(channelId)) { + return this.receivedOutput.get(channelId).getDelta(previousDelta); + } + + return undefined; } private clearOutput(channelId: string): void { - this.receivedOutput.set(channelId, ''); - - this._onOutput.fire({ channelId: channelId, output: null /* indicator to clear output */ }); + if (this.receivedOutput.has(channelId)) { + this.receivedOutput.get(channelId).clear(); + this._onOutput.fire({ channelId: channelId, isClear: true }); + } } private removeOutput(channelId: string): void { @@ -179,10 +235,9 @@ class OutputContentProvider implements ITextModelContentProvider { private static OUTPUT_DELAY = 300; - private bufferedOutput: { [channel: string]: string; }; + private bufferedOutput = new Map(); private appendOutputScheduler: { [channel: string]: RunOnceScheduler; }; private channelIdsWithScrollLock: Set = new Set(); - private toDispose: IDisposable[]; constructor( @@ -191,7 +246,6 @@ class OutputContentProvider implements ITextModelContentProvider { @IModeService private modeService: IModeService, @IPanelService private panelService: IPanelService ) { - this.bufferedOutput = Object.create(null); this.appendOutputScheduler = Object.create(null); this.toDispose = []; @@ -215,15 +269,10 @@ class OutputContentProvider implements ITextModelContentProvider { } // Append to model - if (e.output) { - this.bufferedOutput[e.channelId] = strings.appendWithLimit(this.bufferedOutput[e.channelId] || '', e.output, MAX_OUTPUT_LENGTH); - this.scheduleOutputAppend(e.channelId); - } - - // Clear from model - else if (e.output === null) { - this.bufferedOutput[e.channelId] = ''; + if (e.isClear) { model.setValue(''); + } else { + this.scheduleOutputAppend(e.channelId); } } @@ -236,10 +285,6 @@ class OutputContentProvider implements ITextModelContentProvider { return; // only if the output channel is visible } - if (!this.bufferedOutput[channel]) { - return; // only if we have any output to show - } - let scheduler = this.appendOutputScheduler[channel]; if (!scheduler) { scheduler = new RunOnceScheduler(() => { @@ -274,15 +319,17 @@ class OutputContentProvider implements ITextModelContentProvider { return; // only react if we have a known model } - const bufferedOutput = this.bufferedOutput[channel]; - this.bufferedOutput[channel] = ''; - if (!bufferedOutput) { - return; // return if nothing to append + const bufferedOutput = this.bufferedOutput.get(channel); + const newOutput = this.outputService.getChannel(channel).getOutput(bufferedOutput); + if (!newOutput) { + model.setValue(''); + return; } + this.bufferedOutput.set(channel, newOutput); // just fill in the full (trimmed) output if we exceed max length - if (model.getValueLength() + bufferedOutput.length > MAX_OUTPUT_LENGTH) { - model.setValue(this.outputService.getChannel(channel).output); + if (!newOutput.append) { + model.setValue(newOutput.value); } // otherwise append @@ -290,7 +337,7 @@ class OutputContentProvider implements ITextModelContentProvider { const lastLine = model.getLineCount(); const lastLineMaxColumn = model.getLineMaxColumn(lastLine); - model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), bufferedOutput)]); + model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), newOutput.value)]); } if (!this.channelIdsWithScrollLock.has(channel)) { @@ -319,7 +366,8 @@ class OutputContentProvider implements ITextModelContentProvider { } public provideTextContent(resource: URI): TPromise { - const content = this.outputService.getChannel(resource.fsPath).output; + const output = this.outputService.getChannel(resource.fsPath).getOutput(); + const content = output ? output.value : ''; let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 7b67e67ec1a..2b2a798b09b 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -48,8 +48,8 @@ export const CONTEXT_IN_OUTPUT = new RawContextKey('inOutput', false); * The output event informs when new output got received. */ export interface IOutputEvent { - output: string; - channelId?: string; + channelId: string; + isClear: boolean; } export const IOutputService = createDecorator(OUTPUT_SERVICE_ID); @@ -93,6 +93,12 @@ export interface IOutputService { onActiveOutputChannel: Event; } +export interface IOutputDelta { + readonly value: string; + readonly id: number; + readonly append?: boolean; +} + export interface IOutputChannel { /** @@ -105,11 +111,6 @@ export interface IOutputChannel { */ label: string; - /** - * Returns the received output content. - */ - output: string; - /** * Returns the value indicating whether the channel has scroll locked. */ @@ -120,6 +121,12 @@ export interface IOutputChannel { */ append(output: string): void; + /** + * Returns the received output content. + * If a delta is passed, returns only the content that came after the passed delta. + */ + getOutput(previousDelta?: IOutputDelta): IOutputDelta; + /** * Opens the output for this channel. */ @@ -153,6 +160,11 @@ export interface IOutputChannelRegistry { */ getChannels(): IOutputChannelIdentifier[]; + /** + * Returns the channel with the passed id. + */ + getChannel(id: string): IOutputChannelIdentifier; + /** * Remove the output channel with the passed id. */ @@ -160,24 +172,26 @@ export interface IOutputChannelRegistry { } class OutputChannelRegistry implements IOutputChannelRegistry { - private channels: IOutputChannelIdentifier[]; - - constructor() { - this.channels = []; - } + private channels = new Map(); public registerChannel(id: string, label: string): void { - if (this.channels.every(channel => channel.id !== id)) { - this.channels.push({ id, label }); + if (!this.channels.has(id)) { + this.channels.set(id, { id, label }); } } public getChannels(): IOutputChannelIdentifier[] { - return this.channels; + const result: IOutputChannelIdentifier[] = []; + this.channels.forEach(value => result.push(value)); + return result; + } + + public getChannel(id: string): IOutputChannelIdentifier { + return this.channels.get(id); } public removeChannel(id: string): void { - this.channels = this.channels.filter(channel => channel.id !== id); + this.channels.delete(id); } } diff --git a/src/vs/workbench/parts/output/test/bufferedContent.test.ts b/src/vs/workbench/parts/output/test/bufferedContent.test.ts new file mode 100644 index 00000000000..7f131d5479a --- /dev/null +++ b/src/vs/workbench/parts/output/test/bufferedContent.test.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { BufferedContent } from 'vs/workbench/parts/output/browser/outputServices'; + +suite('Workbench - Output Buffered Content', () => { + + test('Buffered Content - Simple', () => { + const bufferedContent = new BufferedContent(); + bufferedContent.append('first'); + bufferedContent.append('second'); + bufferedContent.append('third'); + const delta = bufferedContent.getDelta(); + assert.equal(bufferedContent.getDelta().value, 'firstsecondthird'); + bufferedContent.clear(); + assert.equal(bufferedContent.getDelta().value, ''); + assert.equal(bufferedContent.getDelta(delta).value, ''); + }); + + test('Buffered Content - Appending Output', () => { + const bufferedContent = new BufferedContent(); + bufferedContent.append('first'); + const firstDelta = bufferedContent.getDelta(); + bufferedContent.append('second'); + bufferedContent.append('third'); + const secondDelta = bufferedContent.getDelta(firstDelta); + assert.equal(secondDelta.append, true); + assert.equal(secondDelta.value, 'secondthird'); + bufferedContent.append('fourth'); + bufferedContent.append('fifth'); + assert.equal(bufferedContent.getDelta(firstDelta).value, 'secondthirdfourthfifth'); + assert.equal(bufferedContent.getDelta(secondDelta).value, 'fourthfifth'); + }); + + test('Buffered Content - Lots of Output', () => { + const bufferedContent = new BufferedContent(); + bufferedContent.append('first line'); + const firstDelta = bufferedContent.getDelta(); + let longString = ''; + for (var i = 0; i < 50000; i++) { + bufferedContent.append(i.toString()); + longString += i.toString(); + } + const secondDelta = bufferedContent.getDelta(firstDelta); + assert.equal(secondDelta.append, true); + assert.equal(secondDelta.value.substr(secondDelta.value.length - 5), '49999'); + longString = longString + longString + longString + longString; + bufferedContent.append(longString); + bufferedContent.append(longString); + const thirdDelta = bufferedContent.getDelta(firstDelta); + assert.equal(!!thirdDelta.append, false); + assert.equal(thirdDelta.value.substr(thirdDelta.value.length - 5), '49999'); + + bufferedContent.clear(); + assert.equal(bufferedContent.getDelta().value, ''); + }); +}); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index c1e9703abfe..c7e3e25ee7b 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -21,7 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService, KeybindingSource, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets'; -import { IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY } from 'vs/workbench/parts/preferences/common/preferences'; +import { IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET } from 'vs/workbench/parts/preferences/common/preferences'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { renderHtml } from 'vs/base/browser/htmlContentRenderer'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; @@ -180,7 +180,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition const options: string[] = [localize('ok', "Ok"), localize('cancel', "Cancel")]; - return this.choiceService.choose(Severity.Info, localize('confirmRemove', "Remove keybinding '{0}' from command '{1}'", keybindingEntry.keybindingItem.keybinding.getAriaLabel(), keybindingEntry.keybindingItem.commandLabel || keybindingEntry.keybindingItem.commandLabel), options, true) + return this.choiceService.choose(Severity.Info, localize('confirmRemove', "Remove keybinding '{0}' from command '{1}'", keybindingEntry.keybindingItem.keybinding.getAriaLabel(), keybindingEntry.keybindingItem.commandLabel || keybindingEntry.keybindingItem.command), options, true) .then(option => { if (option === 0) { return this.keybindingEditingService.removeKeybinding(keybindingEntry.keybindingItem.keybindingItem); @@ -192,6 +192,21 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor return TPromise.as(null); } + resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition + const options: string[] = [localize('ok', "Ok"), localize('cancel', "Cancel")]; + return this.choiceService.choose(Severity.Info, localize('confirmReset', "Reset keybinding for command '{0}'", keybindingEntry.keybindingItem.commandLabel || keybindingEntry.keybindingItem.command), options, true) + .then(option => { + if (option === 0) { + return this.keybindingEditingService.resetKeybinding(keybindingEntry.keybindingItem.keybindingItem); + } + return null; + }) + .then(() => this.focus()); + } + return TPromise.as(null); + } + copyKeybinding(keybinding: IKeybindingItemEntry): TPromise { const userFriendlyKeybinding: IUserFriendlyKeybinding = { command: keybinding.keybindingItem.command, @@ -304,7 +319,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as([this.createCopyAction(e.element), new Separator(), this.createRemoveAction(e.element)]), + getActions: () => TPromise.as([this.createCopyAction(e.element), new Separator(), this.createRemoveAction(e.element), this.createResetAction(e.element)]), getKeyBinding: (action) => this.keybindingsService.lookupKeybinding(action.id) }); } @@ -334,6 +349,15 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor }; } + private createResetAction(keybindingItem: IKeybindingItemEntry): IAction { + return { + label: localize('resetLabel', "Reset Keybinding"), + enabled: !!keybindingItem.keybindingItem.keybinding && keybindingItem.keybindingItem.source === KeybindingSource.User, + id: KEYBINDINGS_EDITOR_COMMAND_RESET, + run: () => this.resetKeybinding(keybindingItem) + }; + } + private createCopyAction(keybindingItem: IKeybindingItemEntry): IAction { return { label: localize('copyLabel', "Copy"), diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index 112004ddc18..c99de751e41 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -18,7 +18,10 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenWorkspaceSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; -import { IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_COPY } from 'vs/workbench/parts/preferences/common/preferences'; +import { + IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, + KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET +} from 'vs/workbench/parts/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -200,6 +203,21 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_RESET, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), + primary: null, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.resetKeybinding(editor.activeKeybindingEntry); + }, + description: { + description: nls.localize('keybindings.editor.reset.description', "Reset Keybinding"), + args: [] + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_SEARCH, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), diff --git a/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts index 2202dd7c031..41e3676be5d 100644 --- a/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/parts/preferences/common/keybindingsEditorModel.ts @@ -86,11 +86,16 @@ export class KeybindingsEditorModel extends EditorModel { editorActions[editorAction.id] = editorAction; return editorActions; }, {}); - this._keybindingItems = this.keybindingsService.getKeybindings().map(keybinding => KeybindingsEditorModel.toKeybindingEntry(keybinding, workbenchActionsRegistry, editorActions)); - const boundCommands: Map = this._keybindingItems.reduce((boundCommands, keybinding) => { - boundCommands.set(keybinding.command, true); - return boundCommands; - }, new Map()); + + this._keybindingItems = []; + const boundCommands: Map = new Map(); + for (const keybinding of this.keybindingsService.getKeybindings()) { + if (keybinding.command) { // Skip keybindings without commands + this._keybindingItems.push(KeybindingsEditorModel.toKeybindingEntry(keybinding, workbenchActionsRegistry, editorActions)); + boundCommands.set(keybinding.command, true); + } + } + for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) { this._keybindingItems.push(KeybindingsEditorModel.toUnassingedKeybindingEntry(command, workbenchActionsRegistry, editorActions)); } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 5bbf0a9179b..6908fa50287 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -89,6 +89,7 @@ export interface IKeybindingsEditor extends IEditor { search(filter: string): void; defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; + resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; copyKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; } @@ -100,4 +101,5 @@ export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding'; +export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_COPY = 'keybindings.editor.copyKeybindingEntry'; \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index f48f801f4c0..d4e378ff5c4 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -60,6 +60,15 @@ background-position: 50% 50%; } +.scm-viewlet .monaco-list .monaco-list-row > .resource > .actions { + display: none; +} + +.scm-viewlet .monaco-list .monaco-list-row.selected > .resource > .actions, +.scm-viewlet .monaco-list:not(.selection-multiple) .monaco-list-row > .resource:hover > .actions { + display: block; +} + .scm-viewlet .monaco-list-row > .resource > .actions .action-label, .scm-viewlet .monaco-list-row > .resource-group > .actions .action-label { width: 16px; @@ -68,10 +77,6 @@ background-repeat: no-repeat; } -.scm-viewlet .monaco-list-row:not(.selected) > .resource:not(:hover) > .actions { - display: none; -} - .scm-viewlet > .scm-editor { padding: 5px 9px 5px 16px; } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts index 6446431418c..07f78ec647a 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmMenus.ts @@ -11,15 +11,15 @@ import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; -import URI from 'vs/base/common/uri'; import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { ISCMService, ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/services/scm/common/scm'; +import { getSCMResourceURI, getSCMResourceGroupId } from './scmUtil'; export class SCMMenus implements IDisposable { private disposables: IDisposable[] = []; - private activeProviderId: string; + private activeProviderId: string | undefined; private titleDisposable: IDisposable = EmptyDisposable; private titleActions: IAction[] = []; private titleSecondaryActions: IAction[] = []; @@ -42,12 +42,12 @@ export class SCMMenus implements IDisposable { this.titleDisposable = EmptyDisposable; } + this.activeProviderId = activeProvider ? activeProvider.id : undefined; + if (!activeProvider) { return; } - this.activeProviderId = activeProvider.id; - const titleMenu = this.menuService.createMenu(MenuId.SCMTitle, this.contextKeyService); const updateActions = () => { this.titleActions = []; @@ -76,52 +76,36 @@ export class SCMMenus implements IDisposable { } getResourceGroupActions(group: ISCMResourceGroup): IAction[] { - return this.getActions(MenuId.SCMResourceGroupContext, this.getSCMResourceGroupURI(group), group.id).primary; + return this.getActions(MenuId.SCMResourceGroupContext, group).primary; } getResourceGroupContextActions(group: ISCMResourceGroup): IAction[] { - return this.getActions(MenuId.SCMResourceGroupContext, this.getSCMResourceGroupURI(group), group.id).secondary; + return this.getActions(MenuId.SCMResourceGroupContext, group).secondary; } getResourceActions(resource: ISCMResource): IAction[] { - return this.getActions(MenuId.SCMResourceContext, this.getSCMResourceURI(resource), resource.resourceGroupId).primary; + return this.getActions(MenuId.SCMResourceContext, resource).primary; } getResourceContextActions(resource: ISCMResource): IAction[] { - return this.getActions(MenuId.SCMResourceContext, this.getSCMResourceURI(resource), resource.resourceGroupId).secondary; - } - - private getSCMResourceGroupURI(resourceGroup: ISCMResourceGroup): URI { - return URI.from({ - scheme: 'scm', - authority: this.activeProviderId, - path: `/${resourceGroup.id}` - }); - } - - private getSCMResourceURI(resource: ISCMResource): URI { - return URI.from({ - scheme: 'scm', - authority: this.activeProviderId, - path: `/${resource.resourceGroupId}/${JSON.stringify(resource.uri)}` - }); + return this.getActions(MenuId.SCMResourceContext, resource).secondary; } private static readonly NoActions = { primary: [], secondary: [] }; - private getActions(menuId: MenuId, context: URI, resourceGroupId: string): { primary: IAction[]; secondary: IAction[]; } { + private getActions(menuId: MenuId, resource: ISCMResourceGroup | ISCMResource): { primary: IAction[]; secondary: IAction[]; } { if (!this.scmService.activeProvider) { return SCMMenus.NoActions; } const contextKeyService = this.contextKeyService.createScoped(); - contextKeyService.createKey('scmResourceGroup', resourceGroupId); + contextKeyService.createKey('scmResourceGroup', getSCMResourceGroupId(resource)); const menu = this.menuService.createMenu(menuId, contextKeyService); const primary = []; const secondary = []; const result = { primary, secondary }; - fillInActions(menu, context, result, g => g === 'inline'); + fillInActions(menu, getSCMResourceURI(this.activeProviderId, resource), result, g => g === 'inline'); menu.dispose(); contextKeyService.dispose(); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts b/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts new file mode 100644 index 00000000000..a9b5b261175 --- /dev/null +++ b/src/vs/workbench/parts/scm/electron-browser/scmUtil.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ISCMResourceGroup, ISCMResource } from 'vs/workbench/services/scm/common/scm'; +import URI from 'vs/base/common/uri'; + +export function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource { + return !!(element as ISCMResource).uri; +} + +export function getSCMResourceURI(providerId: string, resource: ISCMResourceGroup | ISCMResource): URI { + if (isSCMResource(resource)) { + return URI.from({ + scheme: 'scm', + authority: providerId, + path: `/${resource.resourceGroupId}/${JSON.stringify(resource.uri)}` + }); + } else { + return URI.from({ + scheme: 'scm', + authority: providerId, + path: `/${resource.id}` + }); + } +} + +export function getSCMResourceGroupId(resource: ISCMResourceGroup | ISCMResource): string { + return isSCMResource(resource) ? resource.resourceGroupId : resource.id; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index cc2cfb1dca2..c840b5e75ff 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -28,8 +28,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IMessageService } from 'vs/platform/message/common/message'; import { IListService } from 'vs/platform/list/browser/listService'; -import { IMenuService } from 'vs/platform/actions/common/actions'; -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { IAction, IActionItem, ActionRunner } from 'vs/base/common/actions'; import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; import { SCMMenus } from './scmMenus'; import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -37,10 +37,8 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/them import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IModelService } from 'vs/editor/common/services/modelService'; import { comparePaths } from 'vs/base/common/comparers'; - -function isSCMResource(element: ISCMResourceGroup | ISCMResource): element is ISCMResource { - return !!(element as ISCMResource).uri; -} +import URI from 'vs/base/common/uri'; +import { isSCMResource, getSCMResourceURI } from './scmUtil'; function getElementId(element: ISCMResourceGroup | ISCMResource) { if (isSCMResource(element)) { @@ -101,6 +99,30 @@ interface ResourceTemplate { actionBar: ActionBar; } +class MultipleSelectionActionRunner extends ActionRunner { + + constructor(private getSelectedResources: () => URI[]) { + super(); + } + + /** + * Calls the action.run method with the current selection. Note + * that these actions already have the current scm resource context + * within, so we don't want to pass in the selection if there is only + * one selected element. The user should be able to select a single + * item and run an action on another element and only the latter should + * be influenced. + */ + runAction(action: IAction, context?: any): TPromise { + if (action instanceof MenuItemAction) { + const selection = this.getSelectedResources(); + return selection.length > 1 ? action.run(...selection) : action.run(); + } + + return super.runAction(action, context); + } +} + class ResourceRenderer implements IRenderer { static TEMPLATE_ID = 'resource'; @@ -109,6 +131,7 @@ class ResourceRenderer implements IRenderer { constructor( private scmMenus: SCMMenus, private actionItemProvider: IActionItemProvider, + private getSelectedResources: () => URI[], @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IInstantiationService private instantiationService: IInstantiationService ) { } @@ -118,7 +141,11 @@ class ResourceRenderer implements IRenderer { const name = append(element, $('.name')); const fileLabel = this.instantiationService.createInstance(FileLabel, name, void 0); const actionsContainer = append(element, $('.actions')); - const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider }); + const actionBar = new ActionBar(actionsContainer, { + actionItemProvider: this.actionItemProvider, + actionRunner: new MultipleSelectionActionRunner(this.getSelectedResources) + }); + const decorationIcon = append(element, $('.decoration-icon')); return { name, fileLabel, decorationIcon, actionBar }; @@ -166,6 +193,7 @@ export class SCMViewlet extends Viewlet { private listContainer: HTMLElement; private list: List; private menus: SCMMenus; + private activeProviderId: string | undefined; private providerChangeDisposable: IDisposable = EmptyDisposable; private disposables: IDisposable[] = []; @@ -192,6 +220,7 @@ export class SCMViewlet extends Viewlet { private setActiveProvider(activeProvider: ISCMProvider | undefined): void { this.providerChangeDisposable.dispose(); + this.activeProviderId = activeProvider ? activeProvider.id : undefined; if (activeProvider) { this.providerChangeDisposable = activeProvider.onDidChange(this.update, this); @@ -227,9 +256,10 @@ export class SCMViewlet extends Viewlet { const delegate = new Delegate(); const actionItemProvider = action => this.getActionItem(action); + const renderers = [ new ResourceGroupRenderer(this.menus, actionItemProvider), - this.instantiationService.createInstance(ResourceRenderer, this.menus, actionItemProvider), + this.instantiationService.createInstance(ResourceRenderer, this.menus, actionItemProvider, () => this.getSelectedResources()), ]; this.list = new List(this.listContainer, delegate, renderers, { @@ -325,10 +355,16 @@ export class SCMViewlet extends Viewlet { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as(actions) + getActions: () => TPromise.as(actions), + actionRunner: new MultipleSelectionActionRunner(() => this.getSelectedResources()) }); } + private getSelectedResources(): URI[] { + return this.list.getSelectedElements() + .map(r => getSCMResourceURI(this.activeProviderId, r)); + } + dispose(): void { this.disposables = dispose(this.disposables); super.dispose(); diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css index 32815eb2aa8..5288f56d87b 100644 --- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -301,13 +301,13 @@ .vs .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles { background: url('git.svg') center center no-repeat; - background-size: 20px 20px; + background-size: 18px 18px; } .vs-dark .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles, .hc-black .monaco-workbench .search-viewlet .query-details .file-types .controls>.custom-checkbox.useIgnoreFiles { background: url('git-dark.svg') center center no-repeat; - background-size: 20px 20px; + background-size: 18px 18px; } .search-viewlet .findInFileMatch, diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 33161e86911..d9e04d6684a 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -23,7 +23,7 @@ import { ITextModelResolverService, ITextModelContentProvider } from 'vs/editor/ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IModel } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IFileService, isEqual } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; const REPLACE_PREVIEW = 'replacePreview'; @@ -69,7 +69,7 @@ class ReplacePreviewModel extends Disposable { resolve(replacePreviewUri: URI): TPromise { const fileResource = toFileResource(replacePreviewUri); - const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => isEqual(match.resource(), fileResource))[0]; + const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => match.resource().toString() === fileResource.toString())[0]; return this.textModelResolverService.createModelReference(fileResource).then(ref => { ref = this._register(ref); const sourceModel = ref.object.textEditorModel; diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index a5c2c464d29..2eea1e21a4f 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -24,7 +24,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; -import { isEqual } from 'vs/platform/files/common/files'; export class Match { @@ -150,7 +149,7 @@ export class FileMatch extends Disposable { private registerListeners(): void { this._register(this.modelService.onModelAdded((model: IModel) => { - if (isEqual(model.uri, this._resource)) { + if (model.uri.toString() === this._resource.toString()) { this.bindModel(model); } })); @@ -363,7 +362,7 @@ export class SearchResult extends Disposable { fileMatch.onDispose(() => disposable.dispose()); } }); - if (!silent) { + if (!silent && changed.length) { this._onChange.fire({ elements: changed, added: true }); } } @@ -545,14 +544,14 @@ export class SearchModel extends Disposable { public search(query: ISearchQuery): PPromise { this.cancelSearch(); + this._searchQuery = query; + this.currentRequest = this.searchService.search(this._searchQuery); + this.searchResult.clear(); - this._searchQuery = query; this._searchResult.query = this._searchQuery.contentPattern; this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); - this.currentRequest = this.searchService.search(this._searchQuery); - const onDone = fromPromise(this.currentRequest); const onDoneStopwatch = stopwatch(onDone); const start = Date.now(); @@ -565,6 +564,7 @@ export class SearchModel extends Disposable { onFirstRenderStopwatch(duration => this.telemetryService.publicLog('searchResultsFirstRender', { duration })); + const currentRequest = this.currentRequest; this.currentRequest.then( value => this.onSearchCompleted(value, Date.now() - start), e => this.onSearchError(e, Date.now() - start), @@ -574,10 +574,13 @@ export class SearchModel extends Disposable { } ); - return this.currentRequest; + // this.currentRequest may be completed (and nulled) immediately + return currentRequest; } private onSearchCompleted(completed: ISearchComplete, duration: number): ISearchComplete { + this.currentRequest = null; + if (completed) { this._searchResult.add(completed.results, false); } diff --git a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts index 81125067155..e9bd0455676 100644 --- a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts @@ -19,7 +19,7 @@ import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { OS } from 'vs/base/common/platform'; -import { Keybinding } from "vs/base/common/keyCodes"; +import { Keybinding } from 'vs/base/common/keyCodes'; suite('Search Actions', () => { diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts index 07b7f09b73d..385918db51a 100644 --- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -32,18 +32,18 @@ suite('SearchResult', () => { }); test('Line Match', function () { - let fileMatch = aFileMatch('folder\\file.txt', null); + let fileMatch = aFileMatch('folder/file.txt', null); let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); assert.equal(lineMatch.text(), 'foo bar'); assert.equal(lineMatch.range().startLineNumber, 2); assert.equal(lineMatch.range().endLineNumber, 2); assert.equal(lineMatch.range().startColumn, 1); assert.equal(lineMatch.range().endColumn, 4); - assert.equal('file:///c%3A/folder/file.txt>1>0foo', lineMatch.id()); + assert.equal('file:///folder/file.txt>1>0foo', lineMatch.id()); }); test('Line Match - Remove', function () { - let fileMatch = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo bar', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -54,19 +54,19 @@ suite('SearchResult', () => { }); test('File Match', function () { - let fileMatch = aFileMatch('folder\\file.txt'); + let fileMatch = aFileMatch('folder/file.txt'); assert.equal(fileMatch.matches(), 0); - assert.equal(fileMatch.resource().toString(), 'file:///c%3A/folder/file.txt'); + assert.equal(fileMatch.resource().toString(), 'file:///folder/file.txt'); assert.equal(fileMatch.name(), 'file.txt'); fileMatch = aFileMatch('file.txt'); assert.equal(fileMatch.matches(), 0); - assert.equal(fileMatch.resource().toString(), 'file:///c%3A/file.txt'); + assert.equal(fileMatch.resource().toString(), 'file:///file.txt'); assert.equal(fileMatch.name(), 'file.txt'); }); test('File Match: Select an existing match', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -82,7 +82,7 @@ suite('SearchResult', () => { }); test('File Match: Select non existing match', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -100,7 +100,7 @@ suite('SearchResult', () => { }); test('File Match: isSelected return true for selected match', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -116,7 +116,7 @@ suite('SearchResult', () => { }); test('File Match: isSelected return false for un-selected match', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -132,7 +132,7 @@ suite('SearchResult', () => { }); test('File Match: unselect', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -149,7 +149,7 @@ suite('SearchResult', () => { }); test('File Match: unselect when not selected', function () { - let testObject = aFileMatch('folder\\file.txt', aSearchResult(), ...[{ + let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ preview: 'foo', lineNumber: 1, offsetAndLengths: [[0, 3]] @@ -166,7 +166,7 @@ suite('SearchResult', () => { test('Alle Drei Zusammen', function () { let searchResult = instantiationService.createInstance(SearchResult, null); - let fileMatch = aFileMatch('far\\boo', searchResult); + let fileMatch = aFileMatch('far/boo', searchResult); let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); assert(lineMatch.parent() === fileMatch); @@ -360,7 +360,7 @@ suite('SearchResult', () => { function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch { let rawMatch: IFileMatch = { - resource: URI.file('C:\\' + path), + resource: URI.file('/' + path), lineMatches: lineMatches }; return instantiationService.createInstance(FileMatch, null, searchResult, rawMatch); @@ -383,4 +383,4 @@ suite('SearchResult', () => { instantiationService.stub(IConfigurationService, new TestConfigurationService()); return instantiationService.createInstance(ModelServiceImpl); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index f0403783819..55c1fad1f54 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -28,7 +28,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; -import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; import { @@ -389,7 +388,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { shellLaunchConfig.args = []; } } else { - (this.terminalService.configHelper as TerminalConfigHelper).mergeDefaultShellPathAndArgs(shellLaunchConfig); + this.terminalService.configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); } let shellArgs = shellLaunchConfig.args.slice(0); let toAdd: string[] = []; diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index fb6f588f997..d29e23c9dc6 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -64,6 +64,10 @@ export interface ITerminalConfiguration { export interface ITerminalConfigHelper { config: ITerminalConfiguration; getFont(): ITerminalFont; + /** + * Merges the default shell path and args into the provided launch configuration + */ + mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void; } export interface ITerminalFont { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 516d38c88e1..633c1ff52d9 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -64,7 +64,10 @@ configurationRegistry.registerConfiguration({ 'items': { 'type': 'string' }, - 'default': [], + // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This + // is the reason terminals on macOS typically run login shells by default which set up + // the environment. See http://unix.stackexchange.com/a/119675/115410 + 'default': ['-l'], 'isExecutable': true }, 'terminal.integrated.shell.windows': { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index ec930d2ebc2..51e3287c393 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -87,9 +87,8 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { return this._measureFont(fontFamily, fontSize, lineHeight); } - public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): IShellLaunchConfig { + public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void { const config = this.config; - shell.executable = ''; shell.args = []; if (config && config.shell && config.shellArgs) { @@ -104,7 +103,6 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { shell.args = config.shellArgs.linux; } } - return shell; } private _toInteger(source: any, minimum?: number): number { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 1f8736a6098..ff672409e00 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -490,7 +490,9 @@ export class TerminalInstance implements ITerminalInstance { if (message.type === 'data') { this._widgetManager.closeMessage(); this._linkHandler.disposeTooltipListeners(); - this._xterm.write(message.content); + if (this._xterm) { + this._xterm.write(message.content); + } } } diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index d2e76189349..d20e1cac212 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -8,7 +8,6 @@ import * as path from 'path'; import * as crypto from 'crypto'; import pfs = require('vs/base/node/pfs'); -import * as platform from 'vs/base/common/platform'; import Uri from 'vs/base/common/uri'; import { Queue } from 'vs/base/common/async'; import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS } from 'vs/workbench/services/backup/common/backup'; @@ -253,9 +252,6 @@ export class BackupFileService implements IBackupFileService { } private hashPath(resource: Uri): string { - // Windows and Mac paths are case insensitive, we want backups to be too - const caseAwarePath = platform.isWindows || platform.isMacintosh ? resource.fsPath.toLowerCase() : resource.fsPath; - - return crypto.createHash('md5').update(caseAwarePath).digest('hex'); + return crypto.createHash('md5').update(resource.fsPath).digest('hex'); } } diff --git a/src/vs/workbench/services/backup/test/backupFileService.test.ts b/src/vs/workbench/services/backup/test/backupFileService.test.ts index 5bf8ba31b4f..047e0d2b6d8 100644 --- a/src/vs/workbench/services/backup/test/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/backupFileService.test.ts @@ -45,7 +45,7 @@ const barFile = Uri.file(platform.isWindows ? 'c:\\bar' : '/bar'); const untitledFile = Uri.from({ scheme: 'untitled', path: 'Untitled-1' }); const fooBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(fooFile.fsPath).digest('hex')); const barBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(barFile.fsPath).digest('hex')); -const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.createHash('md5').update(platform.isLinux ? untitledFile.fsPath : untitledFile.fsPath.toLowerCase()).digest('hex')); +const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.createHash('md5').update(untitledFile.fsPath).digest('hex')); class TestBackupFileService extends BackupFileService { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { @@ -98,25 +98,10 @@ suite('BackupFileService', () => { // Format should be: /// const backupResource = Uri.from({ scheme: 'untitled', path: 'Untitled-1' }); const workspaceHash = crypto.createHash('md5').update(workspaceResource.fsPath).digest('hex'); - const filePathHash = crypto.createHash('md5').update(platform.isLinux ? backupResource.fsPath : backupResource.fsPath.toLowerCase()).digest('hex'); + const filePathHash = crypto.createHash('md5').update(backupResource.fsPath).digest('hex'); const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; assert.equal(service.getBackupResource(backupResource).fsPath, expectedPath); }); - - test('should ignore case on Windows and Mac', () => { - // Skip test on Linux - if (platform.isLinux) { - return; - } - - if (platform.isMacintosh) { - assert.equal(service.getBackupResource(Uri.file('/foo')).fsPath, service.getBackupResource(Uri.file('/FOO')).fsPath); - } - - if (platform.isWindows) { - assert.equal(service.getBackupResource(Uri.file('c:\\foo')).fsPath, service.getBackupResource(Uri.file('C:\\FOO')).fsPath); - } - }); }); suite('hasBackup', () => { diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index b156102bfa4..e74e57c412a 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -150,7 +150,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi private resolveConfigVariable(value: string, originalValue: string): string { const replacer = (match: string, name: string) => { - let config = this.configurationService.getConfiguration(); + let config = this.configurationService.getConfiguration(); let newValue: any; try { const keys: string[] = name.split('.'); diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts index 6454faf0955..f15a2a8b9da 100644 --- a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import severity from 'vs/base/common/severity'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import dom = require('vs/base/browser/dom'); import { IContextMenuService, IContextMenuDelegate, ContextSubMenu, IEvent } from 'vs/platform/contextview/browser/contextView'; @@ -64,6 +64,7 @@ export class ContextMenuService implements IContextMenuService { private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[]): Electron.Menu { const menu = new remote.Menu(); + const actionRunner = delegate.actionRunner || new ActionRunner(); entries.forEach(e => { if (e instanceof Separator) { @@ -82,7 +83,7 @@ export class ContextMenuService implements IContextMenuService { type: !!e.checked ? 'checkbox' : !!e.radio ? 'radio' : void 0, enabled: !!e.enabled, click: (menuItem, win, event) => { - this.runAction(e, delegate, event); + this.runAction(actionRunner, e, delegate, event); } }; @@ -108,11 +109,11 @@ export class ContextMenuService implements IContextMenuService { return menu; } - private runAction(actionToRun: IAction, delegate: IContextMenuDelegate, event: IEvent): void { + private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IEvent): void { this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); const context = delegate.getActionsContext ? delegate.getActionsContext(event) : event; - const res = actionToRun.run(context) || TPromise.as(null); + const res = actionRunner.run(actionToRun, context) || TPromise.as(null); res.done(null, e => this.messageService.show(severity.Error, e)); } diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index b00931d927e..29a6950db56 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -23,7 +23,7 @@ import extfs = require('vs/base/node/extfs'); import { nfcall, Limiter, ThrottledDelayer } from 'vs/base/common/async'; import uri from 'vs/base/common/uri'; import nls = require('vs/nls'); -import { isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import pfs = require('vs/base/node/pfs'); @@ -433,7 +433,7 @@ export class FileService implements IFileService { // 2.) make sure target is deleted before we move/copy unless this is a case rename of the same file let deleteTargetPromise = TPromise.as(null); if (exists && !isCaseRename) { - if (isEqualOrParent(sourcePath, targetPath)) { + if (isEqualOrParent(sourcePath, targetPath, !isLinux /* ignorecase */)) { return TPromise.wrapError(nls.localize('unableToMoveCopyError', "Unable to move/copy. File would replace folder it is contained in.")); // catch this corner case! } @@ -898,7 +898,7 @@ export class StatResolver { let resolveFolderChildren = false; if (files.length === 1 && resolveSingleChildDescendants) { resolveFolderChildren = true; - } else if (childCount > 0 && absoluteTargetPaths && absoluteTargetPaths.some(targetPath => isEqualOrParent(targetPath, fileResource.fsPath))) { + } else if (childCount > 0 && absoluteTargetPaths && absoluteTargetPaths.some(targetPath => isEqualOrParent(targetPath, fileResource.fsPath, !isLinux /* ignorecase */))) { resolveFolderChildren = true; } diff --git a/src/vs/workbench/services/files/node/watcher/common.ts b/src/vs/workbench/services/files/node/watcher/common.ts index 5b821565616..aa0559b3e77 100644 --- a/src/vs/workbench/services/files/node/watcher/common.ts +++ b/src/vs/workbench/services/files/node/watcher/common.ts @@ -7,6 +7,7 @@ import uri from 'vs/base/common/uri'; import { FileChangeType, FileChangesEvent, isParent } from 'vs/platform/files/common/files'; +import { isLinux } from 'vs/base/common/platform'; export interface IRawFileChange { type: FileChangeType; @@ -105,7 +106,7 @@ class EventNormalizer { }).sort((e1, e2) => { return e1.path.length - e2.path.length; // shortest path first }).filter(e => { - if (deletedPaths.some(d => isParent(e.path, d))) { + if (deletedPaths.some(d => isParent(e.path, d, !isLinux /* ignorecase */))) { return false; // DELETE is ignored if parent is deleted already } diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 5760d240a93..00b0bc6dfbd 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { Queue } from 'vs/base/common/async'; @@ -35,6 +34,8 @@ export interface IKeybindingEditingService { editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise; removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; + + resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; } export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService { @@ -58,6 +59,10 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding return this.queue.queue(() => this.doEditKeybinding(key, keybindingItem)); // queue up writes to prevent race conditions } + resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions + } + removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions } @@ -89,6 +94,18 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding }); } + private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + return this.resolveAndValidate() + .then(reference => { + const model = reference.object.textEditorModel; + if (!keybindingItem.isDefault) { + this.removeUserKeybinding(keybindingItem, model); + this.removeUnassignedDefaultKeybinding(keybindingItem, model); + } + return this.save().then(() => reference.dispose()); + }); + } + private save(): TPromise { return this.textFileService.save(this.resource); } @@ -97,9 +114,9 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding const {tabSize, insertSpaces} = model.getOptions(); const eol = model.getEOL(); const userKeybindingEntries = json.parse(model.getValue()); - const userKeybindingEntry = this.findUserKeybindingEntry(keybindingItem, userKeybindingEntries); - if (userKeybindingEntry) { - this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntries.indexOf(userKeybindingEntry), 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); } } @@ -107,10 +124,10 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding const {tabSize, insertSpaces} = model.getOptions(); const eol = model.getEOL(); const userKeybindingEntries = json.parse(model.getValue()); - const userKeybindingEntry = this.findUserKeybindingEntry(keybindingItem, userKeybindingEntries); - if (userKeybindingEntry) { + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { // Update the keybinding with new key - this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntries.indexOf(userKeybindingEntry), 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model); } else { // Add the new keybinidng with new key this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(newKey, keybindingItem.command, keybindingItem.when, false), { tabSize, insertSpaces, eol })[0], model); @@ -123,11 +140,11 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding private removeUserKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { const {tabSize, insertSpaces} = model.getOptions(); + const eol = model.getEOL(); const userKeybindingEntries = json.parse(model.getValue()); - const userKeybindingEntry = this.findUserKeybindingEntry(keybindingItem, userKeybindingEntries); - if (userKeybindingEntry) { - userKeybindingEntries.splice(userKeybindingEntries.indexOf(userKeybindingEntry), 1); - model.setValue(JSON.stringify(userKeybindingEntries, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t')); + const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (userKeybindingEntryIndex !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex], void 0, { tabSize, insertSpaces, eol })[0], model); } } @@ -137,19 +154,40 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(keybindingItem.resolvedKeybinding.getUserSettingsLabel(), keybindingItem.command, keybindingItem.when, true), { tabSize, insertSpaces, eol })[0], model); } - private findUserKeybindingEntry(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): IUserFriendlyKeybinding { - return userKeybindingEntries.filter(keybinding => { - if (keybinding.command !== keybindingItem.command) { - return false; + private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: editorCommon.IModel): void { + const {tabSize, insertSpaces} = model.getOptions(); + const eol = model.getEOL(); + const userKeybindingEntries = json.parse(model.getValue()); + const index = this.findUnassignedDefaultKeybindingEntryIndex(keybindingItem, userKeybindingEntries); + if (index !== -1) { + this.applyEditsToBuffer(setProperty(model.getValue(), [index], void 0, { tabSize, insertSpaces, eol })[0], model); + } + } + + private findUserKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number { + for (let index = 0; index < userKeybindingEntries.length; index++) { + const keybinding = userKeybindingEntries[index]; + if (keybinding.command === keybindingItem.command) { + if (!keybinding.when && !keybindingItem.when) { + return index; + } + if (keybinding.when && keybindingItem.when) { + if (ContextKeyExpr.deserialize(keybinding.when).serialize() === keybindingItem.when.serialize()) { + return index; + } + } } - if (!keybinding.when && !keybindingItem.when) { - return true; + } + return -1; + } + + private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number { + for (let index = 0; index < userKeybindingEntries.length; index++) { + if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) { + return index; } - if (keybinding.when && keybindingItem.when) { - return ContextKeyExpr.deserialize(keybinding.when).serialize() === keybindingItem.when.serialize(); - } - return false; - })[0]; + } + return -1; } private asObject(key: string, command: string, when: ContextKeyExpr, negate: boolean): any { diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 3605a9f8c1f..f61108001b6 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -10,17 +10,19 @@ import * as path from 'path'; import * as cp from 'child_process'; import { rgPath } from 'vscode-ripgrep'; +import * as extfs from 'vs/base/node/extfs'; import * as encoding from 'vs/base/node/encoding'; import * as strings from 'vs/base/common/strings'; import * as glob from 'vs/base/common/glob'; import { ILineMatch, IProgress } from 'vs/platform/search/common/search'; +import { TPromise } from 'vs/base/common/winjs.base'; import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEngine } from './search'; export class RipgrepEngine implements ISearchEngine { private isDone = false; private rgProc: cp.ChildProcess; - private postProcessExclusions: glob.SiblingClause[]; + private postProcessExclusions: glob.ParsedExpression; private ripgrepParser: RipgrepParser; @@ -47,13 +49,26 @@ export class RipgrepEngine implements ISearchEngine { private searchFolder(rootFolder: string, onResult: (match: ISerializedFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { const rgArgs = getRgArgs(this.config); - this.postProcessExclusions = rgArgs.siblingClauses; + if (rgArgs.siblingClauses) { + this.postProcessExclusions = glob.parseToAsync(rgArgs.siblingClauses, { trimForExclusions: true }); + } // console.log(`rg ${rgArgs.args.join(' ')}, cwd: ${rootFolder}`); this.rgProc = cp.spawn(rgPath, rgArgs.args, { cwd: rootFolder }); this.ripgrepParser = new RipgrepParser(this.config.maxResults, rootFolder); - this.ripgrepParser.on('result', onResult); + this.ripgrepParser.on('result', (match: ISerializedFileMatch) => { + if (this.postProcessExclusions) { + const relativePath = path.relative(rootFolder, match.path); + (>this.postProcessExclusions(relativePath, undefined, () => getSiblings(match.path))).then(globMatch => { + if (!globMatch) { + onResult(match); + } + }); + } else { + onResult(match); + } + }); this.ripgrepParser.on('hitLimit', () => { this.cancel(); done(null, { @@ -281,9 +296,9 @@ export class LineMatch implements ILineMatch { } } -function globExprsToRgGlobs(patterns: glob.IExpression): { globArgs: string[], siblingClauses: glob.SiblingClause[] } { +function globExprsToRgGlobs(patterns: glob.IExpression): { globArgs: string[], siblingClauses: glob.IExpression } { const globArgs: string[] = []; - const siblingClauses: glob.SiblingClause[] = []; + let siblingClauses: glob.IExpression = null; Object.keys(patterns) .forEach(key => { const value = patterns[key]; @@ -295,14 +310,18 @@ function globExprsToRgGlobs(patterns: glob.IExpression): { globArgs: string[], s globArgs.push(key); } else if (value && value.when) { - siblingClauses.push(value); + if (!siblingClauses) { + siblingClauses = {}; + } + + siblingClauses[key] = value; } }); return { globArgs, siblingClauses }; } -function getRgArgs(config: IRawSearch): { args: string[], siblingClauses: glob.SiblingClause[] } { +function getRgArgs(config: IRawSearch): { args: string[], siblingClauses: glob.IExpression } { const args = ['--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold']; args.push(config.contentPattern.isCaseSensitive ? '--case-sensitive' : '--ignore-case'); @@ -313,7 +332,7 @@ function getRgArgs(config: IRawSearch): { args: string[], siblingClauses: glob.S }); } - let siblingClauses: glob.SiblingClause[] = []; + let siblingClauses: glob.IExpression; if (config.excludePattern) { const rgGlobs = globExprsToRgGlobs(config.excludePattern); rgGlobs.globArgs @@ -357,3 +376,15 @@ function getRgArgs(config: IRawSearch): { args: string[], siblingClauses: glob.S return { args, siblingClauses }; } + +function getSiblings(file: string): TPromise { + return new TPromise((resolve, reject) => { + extfs.readdir(path.dirname(file), (error: Error, files: string[]) => { + if (error) { + reject(error); + } + + resolve(files); + }); + }); +} diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 141217aa367..e5db5a60cf0 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -63,23 +63,18 @@ export class SearchService implements ISearchService { let rawSearchQuery: PPromise; return new PPromise((onComplete, onError, onProgress) => { + const searchP = this.diskSearch.search(query); + // Get local results from dirty/untitled - let localResultsFlushed = false; - let localResults = this.getLocalResults(query); + const localResults = this.getLocalResults(query); - let flushLocalResultsOnce = function () { - if (!localResultsFlushed) { - localResultsFlushed = true; - localResults.values().filter((res) => !!res).forEach(onProgress); - } - }; + // Allow caller to register progress callback + process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); - // Delegate to parent for real file results - rawSearchQuery = this.diskSearch.search(query).then( + rawSearchQuery = searchP.then( // on Complete (complete) => { - flushLocalResultsOnce(); onComplete({ limitHit: complete.limitHit, results: complete.results.filter((match) => !localResults.has(match.resource)), // dont override local results @@ -89,13 +84,11 @@ export class SearchService implements ISearchService { // on Error (error) => { - flushLocalResultsOnce(); onError(error); }, // on Progress (progress) => { - flushLocalResultsOnce(); // Match if (progress.resource) { diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 9415699370a..68dbda18efc 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -17,7 +17,6 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { isLinux } from 'vs/base/common/platform'; export class TestTextFileEditorModelManager extends TextFileEditorModelManager { @@ -65,11 +64,7 @@ suite('Files - TextFileEditorModelManager', () => { assert(!manager.get(URI.file('foo'))); assert.strictEqual(manager.get(URI.file('/test.html')), model1); - if (isLinux) { - assert.ok(!manager.get(fileUpper)); - } else { - assert.strictEqual(manager.get(fileUpper), model1); - } + assert.ok(!manager.get(fileUpper)); let result = manager.getAll(); assert.strictEqual(3, result.length); @@ -84,11 +79,7 @@ suite('Files - TextFileEditorModelManager', () => { assert.strictEqual(1, result.length); result = manager.getAll(fileUpper); - if (isLinux) { - assert.strictEqual(0, result.length); - } else { - assert.strictEqual(1, result.length); - } + assert.strictEqual(0, result.length); manager.remove(URI.file('')); @@ -101,11 +92,7 @@ suite('Files - TextFileEditorModelManager', () => { manager.remove(fileUpper); result = manager.getAll(); - if (isLinux) { - assert.strictEqual(2, result.length); - } else { - assert.strictEqual(1, result.length); - } + assert.strictEqual(2, result.length); manager.clear(); result = manager.getAll(); diff --git a/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts index 216b0653547..6d10374bf4a 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts @@ -10,9 +10,8 @@ import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import * as editorColorRegistry from 'vs/editor/common/view/editorColorRegistry'; import * as wordHighlighter from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; import { ansiColorIdentifiers } from 'vs/workbench/parts/terminal/electron-browser/terminalColorRegistry'; -import { editorHoverHighlight } from "vs/editor/contrib/hover/browser/hover"; -import { referencesReferenceHighlight, referencesFindMatchHighlight } from "vs/editor/contrib/referenceSearch/browser/referencesWidget"; - +import { editorHoverHighlight } from 'vs/editor/contrib/hover/browser/hover'; +import { editorPeekReferenceHighlight, editorPeekFindMatchHighlight } from 'vs/editor/contrib/referenceSearch/browser/referencesWidget'; const settingToColorIdMapping: { [settingId: string]: string[] } = {}; function addSettingMapping(settingId: string, colorId: string) { @@ -58,10 +57,10 @@ addSettingMapping('hoverHighlight', editorHoverHighlight); addSettingMapping('hoverHighlight', editorHoverHighlight); addSettingMapping('linkForeground', colorRegistry.editorLinkForeground); addSettingMapping('wordHighlight', wordHighlighter.editorWordHighlight); -addSettingMapping('wordHighlightStrong', wordHighlighter.editorWordHighlightString); +addSettingMapping('wordHighlightStrong', wordHighlighter.editorWordHighlightStrong); addSettingMapping('findRangeHighlight', colorRegistry.editorFindRangeHighlight); -addSettingMapping('findMatchHighlight', referencesFindMatchHighlight); -addSettingMapping('referenceHighlight', referencesReferenceHighlight); +addSettingMapping('findMatchHighlight', editorPeekFindMatchHighlight); +addSettingMapping('referenceHighlight', editorPeekReferenceHighlight); addSettingMapping('lineHighlight', editorColorRegistry.editorLineHighlight); addSettingMapping('rangeHighlight', editorColorRegistry.editorRangeHighlight); addSettingMapping('caret', editorColorRegistry.editorCursor); diff --git a/src/vs/workbench/services/themes/electron-browser/themeService.ts b/src/vs/workbench/services/themes/electron-browser/themeService.ts index 39c83d056c0..2bf4355ee1f 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeService.ts @@ -462,7 +462,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private findThemeData(themeId: string, defaultId?: string): TPromise { return this.getColorThemes().then(allThemes => { - let defaultTheme = void 0; + let defaultTheme: IColorTheme = void 0; for (let t of allThemes) { if (t.id === themeId) { return t; @@ -477,7 +477,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private findThemeDataBySettingsId(settingsId: string, defaultId: string): TPromise { return this.getColorThemes().then(allThemes => { - let defaultTheme = void 0; + let defaultTheme: IColorTheme = void 0; for (let t of allThemes) { if (t.settingsId === settingsId) { return t; @@ -591,10 +591,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } - private themeExtensionsActivated = {}; + private themeExtensionsActivated = new Map(); private sendTelemetry(themeId: string, themeData: ExtensionData, themeType: string) { let key = themeType + themeData.extensionId; - if (!this.themeExtensionsActivated[key]) { + if (!this.themeExtensionsActivated.get(key)) { this.telemetryService.publicLog('activatePlugin', { id: themeData.extensionId, name: themeData.extensionName, @@ -602,7 +602,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { publisherDisplayName: themeData.extensionPublisher, themeId: themeId }); - this.themeExtensionsActivated[key] = true; + this.themeExtensionsActivated.set(key, true); } } diff --git a/src/vs/workbench/test/browser/editorStacksModel.test.ts b/src/vs/workbench/test/browser/editorStacksModel.test.ts index 0ae55a05ad5..0fb97b7e694 100644 --- a/src/vs/workbench/test/browser/editorStacksModel.test.ts +++ b/src/vs/workbench/test/browser/editorStacksModel.test.ts @@ -23,7 +23,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import 'vs/workbench/browser/parts/editor/baseEditor'; -import { isLinux } from 'vs/base/common/platform'; function create(): EditorStacksModel { let inst = new TestInstantiationService(); @@ -1569,17 +1568,10 @@ suite('Editor Stacks Model', () => { assert.equal(model.count(input1Resource), 1); assert.equal(group1.getEditor(input1Resource), input1); - if (isLinux) { - assert.ok(!group1.getEditor(input1ResourceUpper)); - assert.equal(model.count(input1ResourceUpper), 0); - assert.ok(!model.isOpen(input1ResourceUpper)); - assert.ok(!group1.contains(input1ResourceUpper)); - } else { - assert.equal(group1.getEditor(input1ResourceUpper), input1); - assert.equal(model.count(input1ResourceUpper), 1); - assert.ok(model.isOpen(input1ResourceUpper)); - assert.ok(group1.contains(input1ResourceUpper)); - } + assert.ok(!group1.getEditor(input1ResourceUpper)); + assert.equal(model.count(input1ResourceUpper), 0); + assert.ok(!model.isOpen(input1ResourceUpper)); + assert.ok(!group1.contains(input1ResourceUpper)); group2.openEditor(input1); group1.closeEditor(input1); diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts deleted file mode 100644 index e096e1a4383..00000000000 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import 'vs/workbench/parts/search/browser/search.contribution'; // load contributions -import * as assert from 'assert'; -import * as fs from 'fs'; -import { WorkspaceContextService, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search'; -import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry'; -import { defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import * as minimist from 'minimist'; -import * as path from 'path'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { TestEnvironmentService, TestEditorService, TestEditorGroupService } from 'vs/workbench/test/workbenchTestServices'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { SimpleConfigurationService } from 'vs/editor/browser/standalone/simpleServices'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IModelService } from 'vs/editor/common/services/modelService'; - -import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; -import { QueryBuilder } from 'vs/workbench/parts/search/common/searchQuery'; - -import Event, * as event from 'vs/base/common/event'; - -declare var __dirname: string; - -// Checkout sources to run against: -// git clone --separate-git-dir=testGit --no-checkout --single-branch https://chromium.googlesource.com/chromium/src testWorkspace -// cd testWorkspace; git checkout 39a7f93d67f7 -// Run from repository root folder with (test.bat on Windows): ./scripts/test.sh --grep TextSearch.performance --timeout 500000 --testWorkspace -suite('TextSearch performance', () => { - - test('Measure', () => { - if (process.env['VSCODE_PID']) { - return undefined; // TODO@Rob find out why test fails when run from within VS Code - } - - const n = 3; - const argv = minimist(process.argv); - const testWorkspaceArg = argv['testWorkspace']; - const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname; - if (!fs.existsSync(testWorkspacePath)) { - throw new Error(`--testWorkspace doesn't exist`); - } - - const telemetryService = new TestTelemetryService(); - const configurationService = new SimpleConfigurationService(); - const instantiationService = new InstantiationService(new ServiceCollection( - [ITelemetryService, telemetryService], - [IConfigurationService, new SimpleConfigurationService()], - [IModelService, new ModelServiceImpl(null, configurationService)], - [IWorkspaceContextService, new WorkspaceContextService({ resource: URI.file(testWorkspacePath) })], - [IWorkbenchEditorService, new TestEditorService()], - [IEditorGroupService, new TestEditorGroupService()], - [IEnvironmentService, TestEnvironmentService], - [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], - [ISearchService, createSyncDescriptor(SearchService)] - )); - - let queryOptions: IQueryOptions = { - folderResources: [URI.file(testWorkspacePath)], - maxResults: 2048 - }; - - const searchModel: SearchModel = instantiationService.createInstance(SearchModel); - function runSearch(): TPromise { - const queryBuilder: QueryBuilder = instantiationService.createInstance(QueryBuilder); - const query = queryBuilder.text({ pattern: 'static_library(' }, queryOptions); - - // Wait for the 'searchResultsFinished' event, which is fired after the search() promise is resolved - const onSearchResultsFinished = event.filterEvent(telemetryService.eventLogged, e => e.name === 'searchResultsFinished'); - event.once(onSearchResultsFinished)(onComplete); - - function onComplete(): void { - try { - const allEvents = telemetryService.events.map(e => JSON.stringify(e)).join('\n'); - assert.equal(telemetryService.events.length, 3, 'Expected 3 telemetry events, got:\n' + allEvents); - - const [firstRenderEvent, resultsShownEvent, resultsFinishedEvent] = telemetryService.events; - assert.equal(firstRenderEvent.name, 'searchResultsFirstRender'); - assert.equal(resultsShownEvent.name, 'searchResultsShown'); - assert.equal(resultsFinishedEvent.name, 'searchResultsFinished'); - - telemetryService.events = []; - - resolve(resultsFinishedEvent); - } catch (e) { - // Fail the runSearch() promise - error(e); - } - } - - let resolve; - let error; - return new TPromise((_resolve, _error) => { - resolve = _resolve; - error = _error; - - // Don't wait on this promise, we're waiting on the event fired above - searchModel.search(query).then( - null, - _error); - }); - } - - const finishedEvents = []; - return runSearch() // Warm-up first - .then(() => { - if (testWorkspaceArg) { // Don't measure by default - let i = n; - return (function iterate() { - if (!i--) { - return; - } - - return runSearch() - .then((resultsFinishedEvent: any) => { - console.log(`Iteration ${n - i}: ${resultsFinishedEvent.data.duration / 1000}s`); - finishedEvents.push(resultsFinishedEvent); - return iterate(); - }); - })().then(() => { - const totalTime = finishedEvents.reduce((sum, e) => sum + e.data.duration, 0); - console.log(`Avg duration: ${totalTime / n / 1000}s`); - }); - } - }); - }); -}); - -class TestTelemetryService implements ITelemetryService { - public _serviceBrand: any; - public isOptedIn = true; - - public events: any[] = []; - - private emitter = new event.Emitter(); - - public get eventLogged(): Event { - return this.emitter.event; - } - - public publicLog(eventName: string, data?: any): TPromise { - const event = { name: eventName, data: data }; - this.events.push(event); - this.emitter.fire(event); - return TPromise.as(null); - } - - public getTelemetryInfo(): TPromise { - return TPromise.as({ - instanceId: 'someValue.instanceId', - sessionId: 'someValue.sessionId', - machineId: 'someValue.machineId' - }); - } - - public getExperiments(): ITelemetryExperiments { - return defaultExperiments; - } -}; diff --git a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts index 9aecac17051..938952d2a10 100644 --- a/src/vs/workbench/test/node/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/node/api/extHostApiCommands.test.ts @@ -306,7 +306,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let values = list.items; assert.ok(Array.isArray(values)); assert.equal(values.length, 4); - let [first, second, third, forth] = values; + let [first, second, third, fourth] = values; assert.equal(first.label, 'item1'); assert.equal(first.textEdit.newText, 'item1'); assert.equal(first.textEdit.range.start.line, 0); @@ -328,14 +328,14 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.equal(third.textEdit.range.end.line, 0); assert.equal(third.textEdit.range.end.character, 6); - assert.equal(forth.label, 'item4'); - assert.equal(forth.textEdit, undefined); - assert.equal(forth.range.start.line, 0); - assert.equal(forth.range.start.character, 1); - assert.equal(forth.range.end.line, 0); - assert.equal(forth.range.end.character, 4); - assert.ok(forth.insertText instanceof types.SnippetString); - assert.equal((forth.insertText).value, 'foo$0bar'); + assert.equal(fourth.label, 'item4'); + assert.equal(fourth.textEdit, undefined); + assert.equal(fourth.range.start.line, 0); + assert.equal(fourth.range.start.character, 1); + assert.equal(fourth.range.end.line, 0); + assert.equal(fourth.range.end.character, 4); + assert.ok(fourth.insertText instanceof types.SnippetString); + assert.equal((fourth.insertText).value, 'foo$0bar'); }); }); }); diff --git a/src/vs/workbench/test/node/api/extHostDocumentData.test.ts b/src/vs/workbench/test/node/api/extHostDocumentData.test.ts index 4ddad3fdace..dc5eb511d62 100644 --- a/src/vs/workbench/test/node/api/extHostDocumentData.test.ts +++ b/src/vs/workbench/test/node/api/extHostDocumentData.test.ts @@ -11,6 +11,8 @@ import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; import { Position } from 'vs/workbench/api/node/extHostTypes'; import { Range as CodeEditorRange } from 'vs/editor/common/core/range'; import * as EditorCommon from 'vs/editor/common/editorCommon'; +import { MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; suite('ExtHostDocumentData', () => { @@ -18,14 +20,14 @@ suite('ExtHostDocumentData', () => { let data: ExtHostDocumentData; function assertPositionAt(offset: number, line: number, character: number) { - let position = data.positionAt(offset); + let position = data.document.positionAt(offset); assert.equal(position.line, line); assert.equal(position.character, character); } function assertOffsetAt(line: number, character: number, offset: number) { let pos = new Position(line, character); - let actual = data.offsetAt(pos); + let actual = data.document.offsetAt(pos); assert.equal(actual, offset); } @@ -47,17 +49,48 @@ suite('ExtHostDocumentData', () => { assert.throws(() => (data).document.lineCount = 9); }); + test('save, when disposed', function () { + let saved: URI; + let data = new ExtHostDocumentData(new class extends MainThreadDocumentsShape { + $trySaveDocument(uri) { + assert.ok(!saved); + saved = uri; + return TPromise.as(true); + } + }, URI.parse('foo:bar'), [], '\n', 'text', 1, true); + + return data.document.save().then(() => { + assert.equal(saved.toString(), 'foo:bar'); + + data.dispose(); + + return data.document.save().then(() => { + assert.ok(false, 'expected failure'); + }, err => { + assert.ok(err); + }); + }); + }); + + test('read, when disposed', function () { + data.dispose(); + + const { document } = data; + assert.equal(document.lineCount, 4); + assert.equal(document.lineAt(0).text, 'This is line one'); + }); + test('lines', function () { assert.equal(data.document.lineCount, 4); - assert.throws(() => data.lineAt(-1)); - assert.throws(() => data.lineAt(data.document.lineCount)); - assert.throws(() => data.lineAt(Number.MAX_VALUE)); - assert.throws(() => data.lineAt(Number.MIN_VALUE)); - assert.throws(() => data.lineAt(0.8)); + assert.throws(() => data.document.lineAt(-1)); + assert.throws(() => data.document.lineAt(data.document.lineCount)); + assert.throws(() => data.document.lineAt(Number.MAX_VALUE)); + assert.throws(() => data.document.lineAt(Number.MIN_VALUE)); + assert.throws(() => data.document.lineAt(0.8)); - let line = data.lineAt(0); + let line = data.document.lineAt(0); assert.equal(line.lineNumber, 0); assert.equal(line.text.length, 16); assert.equal(line.text, 'This is line one'); @@ -79,7 +112,7 @@ suite('ExtHostDocumentData', () => { assert.equal(line.firstNonWhitespaceCharacterIndex, 0); // fetch line again - line = data.lineAt(0); + line = data.document.lineAt(0); assert.equal(line.text, '\t This is line one'); assert.equal(line.firstNonWhitespaceCharacterIndex, 2); }); @@ -208,32 +241,32 @@ suite('ExtHostDocumentData', () => { 'aaaa bbbb+cccc abc' ], '\n', 'text', 1, false); - let range = data.getWordRangeAtPosition(new Position(0, 2)); + let range = data.document.getWordRangeAtPosition(new Position(0, 2)); assert.equal(range.start.line, 0); assert.equal(range.start.character, 0); assert.equal(range.end.line, 0); assert.equal(range.end.character, 4); // ignore bad regular expresson /.*/ - range = data.getWordRangeAtPosition(new Position(0, 2), /.*/); + range = data.document.getWordRangeAtPosition(new Position(0, 2), /.*/); assert.equal(range.start.line, 0); assert.equal(range.start.character, 0); assert.equal(range.end.line, 0); assert.equal(range.end.character, 4); - range = data.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/); + range = data.document.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/); assert.equal(range.start.line, 0); assert.equal(range.start.character, 5); assert.equal(range.end.line, 0); assert.equal(range.end.character, 14); - range = data.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/); + range = data.document.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/); assert.equal(range.start.line, 0); assert.equal(range.start.character, 15); assert.equal(range.end.line, 0); assert.equal(range.end.character, 18); - range = data.getWordRangeAtPosition(new Position(0, 11), /yy/); + range = data.document.getWordRangeAtPosition(new Position(0, 11), /yy/); assert.equal(range, undefined); }); }); @@ -258,12 +291,12 @@ suite('ExtHostDocumentData updates line mapping', () => { let position = new Position(line, character + (previousIsCarriageReturn ? -1 : 0)); if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) { - let actualPosition = doc.positionAt(offset); + let actualPosition = doc.document.positionAt(offset); assert.equal(positionToStr(actualPosition), positionToStr(position), 'positionAt mismatch for offset ' + offset); } else { // The position coordinate system cannot express the position between \r and \n let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0); - let actualOffset = doc.offsetAt(position); + let actualOffset = doc.document.offsetAt(position); assert.equal(actualOffset, expectedOffset, 'offsetAt mismatch for position ' + positionToStr(position)); } diff --git a/src/vs/workbench/test/node/api/mainThreadDocuments.test.ts b/src/vs/workbench/test/node/api/mainThreadDocuments.test.ts new file mode 100644 index 00000000000..334b12318bb --- /dev/null +++ b/src/vs/workbench/test/node/api/mainThreadDocuments.test.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { BoundModelReferenceCollection } from 'vs/workbench/api/node/mainThreadDocuments'; +import { Model } from 'vs/editor/common/model/model'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('BoundModelReferenceCollection', () => { + + let col = new BoundModelReferenceCollection(15, 75); + + teardown(() => { + col.dispose(); + }); + + test('max age', () => { + + let didDispose = false; + + col.add({ + object: { textEditorModel: Model.createFromString('farboo') }, + dispose() { + didDispose = true; + } + }); + + return TPromise.timeout(30).then(() => { + assert.equal(didDispose, true); + }); + }); + + test('max size', () => { + + let disposed: number[] = []; + + col.add({ + object: { textEditorModel: Model.createFromString('farboo') }, + dispose() { + disposed.push(0); + } + }); + col.add({ + object: { textEditorModel: Model.createFromString('boofar') }, + dispose() { + disposed.push(1); + } + }); + + col.add({ + object: { textEditorModel: Model.createFromString(new Array(71).join('x')) }, + dispose() { + disposed.push(2); + } + }); + + assert.deepEqual(disposed, [0, 1]); + }); + +}); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index fc2f38f11ef..586b7623068 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -53,6 +53,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IThemeService, ITheme, IThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; +import { isLinux } from 'vs/base/common/platform'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, void 0); @@ -93,7 +94,7 @@ export class TestContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return isEqualOrParent(resource.fsPath, this.workspace.resource.fsPath); + return isEqualOrParent(resource.fsPath, this.workspace.resource.fsPath, !isLinux /* ignorecase */); } return false; @@ -984,7 +985,6 @@ export class TestWindowsService implements IWindowsService { export class TestTheme implements ITheme { selector: string; - label: string; type: 'light' | 'dark' | 'hc'; getColor(color: string, useDefault?: boolean): Color { diff --git a/tslint.json b/tslint.json index 084f4addff2..aae82a05a27 100644 --- a/tslint.json +++ b/tslint.json @@ -56,6 +56,15 @@ "restrictions": "**/vs/**" } ], - "duplicate-imports": true + "duplicate-imports": true, + "allow-async": [ + true, + [ + "node", + "electron-main", + "electron-browser", + "extensions" + ] + ] } -} +} \ No newline at end of file