Merge branch 'master' into electron-2.0.x

This commit is contained in:
Benjamin Pasero 2018-06-29 14:53:43 +02:00
commit 59c29b42db
233 changed files with 6088 additions and 2257 deletions

View file

@ -1,43 +1,69 @@
{
perform: true,
alwaysRequireAssignee: false,
labelsRequiringAssignee: [ feature-request ],
labelsRequiringAssignee: [],
autoAssignees: {
L10N: [],
VIM: [],
api: {
assignees: [ jrieken ],
assignLabel: false
},
color-picker: [],
css-less-sass: [ aeschli ],
cli: [],
color-palette: [],
config: [],
css-less-scss: [ aeschli ],
debug-console: [],
debug: {
assignees: [ weinand ],
assignLabel: false
},
diff-editor: [],
dropdown: [],
editor: [],
editor-1000-limit: [],
editor-autoclosing: [],
editor-autoindent: [],
editor-brackets: [],
editor-clipboard: [],
editor-code-actions: [],
editor-code-lens: [],
editor-color-picker: [],
editor-colors: [],
editor-columnselect: [],
editor-commands: [],
editor-contrib: [],
editor-core: [],
editor-find-widget: [],
editor-drag-and-drop: [],
editor-find: [],
editor-folding: [],
editor-hover: [],
editor-ime: [],
editor-input: [],
editor-ligatures: [],
editor-links: [],
editor-minimap: [],
editor-multicursor: [],
editor-parameter-hints: [],
editor-rendering: [],
editor-smooth: [],
editor-symbols: [],
editor-textbuffer: [],
editor-wrapping: [],
emmet: [ ramya-rao-a ],
error-list: [],
explorer-custom: [],
extension-host: [],
extensions: [],
file-decorations: [],
file-encoding: {
assignees: [],
assignLabel: false
},
file-explorer: {
assignees: [ isidorn ],
assignLabel: false
},
file-glob: [],
file-io: {
assignees: [],
assignLabel: false
@ -46,45 +72,73 @@
assignees: [],
assignLabel: false
},
file-explorer: {
assignees: [ isidorn ],
assignLabel: false
},
format: [],
formatting: [],
git: [ joaomoreno ],
grammar: [],
hot-exit: [ Tyriar ],
html: [ aeschli ],
i18n: [],
install-update: [],
integrated-terminal: [ Tyriar ],
integration-test: [],
intellisense-config: [],
issue-reporter: [ RMacfarlane ],
javascript: [ mjbvz ],
json: [],
keybindings: [],
keyboard-layout: [],
keybindings: [],
keybindings-editor: [],
lang-diagnostics: [],
languages basic: [],
list: [],
log: [],
markdown: [ mjbvz ],
marketplace: [],
menus: [],
merge-conflict: [ chrmarti ],
multi-root: {
assignees: [],
assignLabel: false
},
os-integration: [],
outline: [],
output: [],
perf-profile: [],
perf-bloat: [],
perf-startup: [],
php: [ roblourens ],
proxy: [],
quick-pick: [ chrmarti ],
release-notes: [],
remote: {
assignees: [ jrieken ],
assignLabel: false
},
rename: [],
run-as-admin: [],
samples: [],
scm: [],
search: [ roblourens ],
search-replace: [],
settings-editor: [],
shared-process: [],
smart-select: [],
smoke-test: [],
snippets: {
assignees: [ jrieken ],
assignLabel: false
},
suggest: [],
tasks: [ dbaeumer ],
telemetry: [],
themes: [],
tokenization: [],
tree: [],
typescript: [ mjbvz ],
unit-test: [],
uri: [],
ux: [],
vscode-build: [],
webview: [],
workbench: {
assignees: [],
assignLabel: false
@ -109,6 +163,10 @@
assignees: [],
assignLabel: false
},
workbench-grid: {
assignees: [],
assignLabel: false
},
workbench-history: {
assignees: [],
assignLabel: false
@ -145,6 +203,10 @@
assignees: [],
assignLabel: false
},
workbench-views: {
assignees: [],
assignLabel: false
},
workbench-welcome: [ chrmarti ]
}
}

View file

@ -1,12 +1,12 @@
[
{
"name": "ms-vscode.node-debug",
"version": "1.25.3",
"version": "1.25.4",
"repo": "https://github.com/Microsoft/vscode-node-debug"
},
{
"name": "ms-vscode.node-debug2",
"version": "1.25.4",
"version": "1.25.5",
"repo": "https://github.com/Microsoft/vscode-node-debug2"
}
]

View file

@ -71,7 +71,7 @@ const vscodeResources = [
'out-build/paths.js',
'out-build/vs/**/*.{svg,png,cur,html}',
'out-build/vs/base/common/performance.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh, cpuUsage.sh}',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh}',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/electron-browser/bootstrap/**',
@ -508,6 +508,10 @@ gulp.task('generate-vscode-configuration', () => {
return reject(new Error('$AGENT_BUILDDIRECTORY not set'));
}
if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') {
return resolve();
}
const userDataDir = path.join(os.tmpdir(), 'tmpuserdata');
const extensionsDir = path.join(os.tmpdir(), 'tmpextdir');
const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app';

View file

@ -74,14 +74,15 @@ function buildWin32Setup(arch, target) {
DirName: product.win32DirName,
Version: pkg.version,
RawVersion: pkg.version.replace(/-\w+$/, ''),
NameVersion: product.win32NameVersion,
NameVersion: product.win32NameVersion + (target === 'user' ? ' (User)' : ''),
ExeBasename: product.nameShort,
RegValueName: product.win32RegValueName,
ShellNameShort: product.win32ShellNameShort,
AppMutex: product.win32MutexName,
Arch: arch,
AppId: arch === 'ia32' ? ia32AppId : x64AppId,
IncompatibleAppId: arch === 'ia32' ? x64AppId : ia32AppId,
IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId,
IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId,
AppUserId: product.win32AppUserModelId,
ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64',
ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64',
@ -108,7 +109,7 @@ defineWin32SetupTasks('x64', 'user');
function archiveWin32Setup(arch) {
return cb => {
const args = ['a', '-tzip', zipPath(arch), '.', '-r'];
const args = ['a', '-tzip', zipPath(arch), '-x!CodeSignSummary*.md', '.', '-r'];
cp.spawn(_7z, args, { stdio: 'inherit', cwd: buildPath(arch) })
.on('error', cb)

View file

@ -547,7 +547,7 @@
},
{
"name": "retep998/winapi-rs",
"version": "0.3.4",
"version": "0.4.0",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",
@ -599,6 +599,60 @@
],
"isProd": true
},
{
"name": "retep998/winapi-rs",
"version": "0.2.2",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy",
"of this software and associated documentation files (the \"Software\"), to deal",
"in the Software without restriction, including without limitation the rights",
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
"copies of the Software, and to permit persons to whom the Software is",
"furnished to do so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE."
],
"isProd": true
},
{
"name": "retep998/winapi-rs",
"version": "0.4.0",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy",
"of this software and associated documentation files (the \"Software\"), to deal",
"in the Software without restriction, including without limitation the rights",
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
"copies of the Software, and to permit persons to whom the Software is",
"furnished to do so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE."
],
"isProd": true
},
{
"name": "retep998/winapi-rs",
"version": "0.1.1",
@ -628,61 +682,7 @@
},
{
"name": "retep998/winapi-rs",
"version": "0.4.0",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy",
"of this software and associated documentation files (the \"Software\"), to deal",
"in the Software without restriction, including without limitation the rights",
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
"copies of the Software, and to permit persons to whom the Software is",
"furnished to do so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE."
],
"isProd": true
},
{
"name": "retep998/winapi-rs",
"version": "0.4.0",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy",
"of this software and associated documentation files (the \"Software\"), to deal",
"in the Software without restriction, including without limitation the rights",
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
"copies of the Software, and to permit persons to whom the Software is",
"furnished to do so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE."
],
"isProd": true
},
{
"name": "retep998/winapi-rs",
"version": "0.2.2",
"version": "0.3.4",
"repositoryUrl": "https://github.com/retep998/winapi-rs",
"licenseDetail": [
"Copyright (c) 2015 The winapi-rs Developers",

View file

@ -963,8 +963,26 @@ var
begin
Result := True;
if IsWin64 then begin
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleAppId}', 2, 38) + '_is1';
#if "user" == InstallTarget
#if "ia32" == Arch
#define IncompatibleArchRootKey "HKLM32"
#else
#define IncompatibleArchRootKey "HKLM64"
#endif
if not WizardSilent() then begin
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleTargetAppId}', 2, 38) + '_is1';
if RegKeyExists({#IncompatibleArchRootKey}, RegKey) then begin
if MsgBox('{#NameShort} is already installed on this system for all users. Are you sure you want to install it for this user?', mbConfirmation, MB_YESNO) = IDNO then begin
Result := false;
end;
end;
end;
#endif
if Result and IsWin64 then begin
RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleArchAppId}', 2, 38) + '_is1';
if '{#Arch}' = 'ia32' then begin
Result := not RegKeyExists({#Uninstall64RootKey}, RegKey);

Binary file not shown.

View file

@ -8,7 +8,7 @@
"node": "*"
},
"dependencies": {
"vscode-css-languageservice": "^3.0.9-next.18",
"vscode-css-languageservice": "^3.0.9-next.20",
"vscode-languageserver": "^4.1.3",
"vscode-languageserver-protocol-foldingprovider": "^2.0.1"
},

View file

@ -194,9 +194,9 @@ supports-color@5.4.0:
dependencies:
has-flag "^3.0.0"
vscode-css-languageservice@^3.0.9-next.18:
version "3.0.9-next.18"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.18.tgz#f8f25123b5a8cdc9f72fafcd2c0088f726322437"
vscode-css-languageservice@^3.0.9-next.20:
version "3.0.9-next.20"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e"
dependencies:
vscode-languageserver-types "^3.7.2"
vscode-nls "^3.2.2"

View file

@ -8,7 +8,7 @@
"vscode": "0.10.x"
},
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js atom/language-css grammars/css.cson ./syntaxes/css.tmLanguage.json"
"update-grammar": "node ../../build/npm/update-grammar.js octref/language-css grammars/css.cson ./syntaxes/css.tmLanguage.json"
},
"contributes": {
"languages": [

View file

@ -1,10 +1,10 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/atom/language-css/blob/master/grammars/css.cson",
"This file has been converted from https://github.com/octref/language-css/blob/master/grammars/css.cson",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/atom/language-css/commit/17ad55bc5f65c16585e80ea1c7c19e0c0814f6d5",
"version": "https://github.com/octref/language-css/commit/ea1d7e3619966e47c57498913a5eabea0cce7538",
"name": "CSS",
"scopeName": "source.css",
"patterns": [
@ -604,6 +604,45 @@
"include": "#string"
}
]
},
{
"begin": "(?i)(?=@[\\w-]+(\\s|\\(|/\\*|$))",
"end": "(?<=})(?!\\G)",
"patterns": [
{
"begin": "(?i)\\G(@)[\\w-]+",
"beginCaptures": {
"0": {
"name": "keyword.control.at-rule.css"
},
"1": {
"name": "punctuation.definition.keyword.css"
}
},
"end": "(?=\\s*[{;])",
"name": "meta.at-rule.header.css"
},
{
"begin": "{",
"beginCaptures": {
"0": {
"name": "punctuation.section.begin.bracket.curly.css"
}
},
"end": "}",
"endCaptures": {
"0": {
"name": "punctuation.section.end.bracket.curly.css"
}
},
"name": "meta.at-rule.body.css",
"patterns": [
{
"include": "$self"
}
]
}
]
}
]
},
@ -1411,7 +1450,7 @@
"name": "invalid.illegal.colon.css"
}
},
"match": "(?xi)\n(:)(:*)\n(?: active|any-link|checked|default|disabled|empty|enabled|first\n | (?:first|last|only)-(?:child|of-type)|focus|focus-within|fullscreen|host|hover\n | in-range|indeterminate|invalid|left|link|optional|out-of-range\n | read-only|read-write|required|right|root|scope|target|unresolved\n | valid|visited\n)(?![\\w-]|\\s*[;}])",
"match": "(?xi)\n(:)(:*)\n(?: active|any-link|checked|default|defined|disabled|empty|enabled|first\n | (?:first|last|only)-(?:child|of-type)|focus|focus-visible|focus-within\n | fullscreen|host|hover|in-range|indeterminate|invalid|left|link\n | optional|out-of-range|placeholder-shown|read-only|read-write\n | required|right|root|scope|target|unresolved\n | valid|visited\n)(?![\\w-]|\\s*[;}])",
"name": "entity.other.attribute-name.pseudo-class.css"
},
"pseudo-elements": {

View file

@ -31,7 +31,7 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
// Get the first child of current node which is right after the cursor and is not a comment
nextNode = currentNode.firstChild;
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.start) || nextNode.type === 'comment')) {
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.end) || nextNode.type === 'comment')) {
nextNode = nextNode.nextSibling;
}
}
@ -63,7 +63,7 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
if (currentNode.type !== 'comment' && selectionStart.translate(0, -1).isAfter(currentNode.open.start)) {
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild) {
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild || selectionEnd.isBeforeOrEqual(currentNode.firstChild.start)) {
prevNode = currentNode;
} else {
// Select the child that appears just before the cursor and is not a comment

View file

@ -113,6 +113,22 @@ suite('Tests for Next/Previous Select/Edit point and Balance actions', () => {
});
});
test('Emmet Select Next/Prev item at boundary', function(): any {
return withRandomFileEditor(htmlContents, '.html', (editor, doc) => {
editor.selections = [new Selection(4, 1, 4, 1)];
fetchSelectItem('next');
testSelection(editor.selection, 2, 4, 6);
editor.selections = [new Selection(4, 1, 4, 1)];
fetchSelectItem('prev');
testSelection(editor.selection, 1, 3, 5);
return Promise.resolve();
});
});
test('Emmet Next/Prev Item in html template', function (): any {
const templateContents = `
<script type="text/template">

View file

@ -1,8 +1,8 @@
{
"name": "grunt",
"publisher": "vscode",
"description": "Extension to add Grunt capabilities to VSCode.",
"displayName": "Grunt support for VSCode",
"description": "Extension to add Grunt capabilities to VS Code.",
"displayName": "Grunt support for VS Code",
"version": "1.0.0",
"icon": "images/grunt.png",
"engines": {

View file

@ -1,6 +1,6 @@
{
"description": "Extension to add Grunt capabilities to VSCode.",
"displayName": "Grunt support for VSCode",
"description": "Extension to add Grunt capabilities to VS Code.",
"displayName": "Grunt support for VS Code",
"config.grunt.autoDetect": "Controls whether auto detection of Grunt tasks is on or off. Default is on.",
"grunt.taskDefinition.type.description": "The Grunt task to customize.",
"grunt.taskDefinition.file.description": "The Grunt file that provides the task. Can be omitted."

View file

@ -8,7 +8,7 @@
"node": "*"
},
"dependencies": {
"vscode-css-languageservice": "^3.0.9-next.18",
"vscode-css-languageservice": "^3.0.9-next.20",
"vscode-html-languageservice": "^2.1.3-next.5",
"vscode-languageserver": "^4.1.3",
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",

View file

@ -194,9 +194,9 @@ supports-color@5.4.0:
dependencies:
has-flag "^3.0.0"
vscode-css-languageservice@^3.0.9-next.18:
version "3.0.9-next.18"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.18.tgz#f8f25123b5a8cdc9f72fafcd2c0088f726322437"
vscode-css-languageservice@^3.0.9-next.20:
version "3.0.9-next.20"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e"
dependencies:
vscode-languageserver-types "^3.7.2"
vscode-nls "^3.2.2"

View file

@ -1,6 +1,6 @@
{
"description": "Extension to add Jake capabilities to VSCode.",
"displayName": "Jake support for VSCode",
"description": "Extension to add Jake capabilities to VS Code.",
"displayName": "Jake support for VS Code",
"jake.taskDefinition.type.description": "The Jake task to customize.",
"jake.taskDefinition.file.description": "The Jake file that provides the task. Can be omitted.",
"config.jake.autoDetect": "Controls whether auto detection of Jake tasks is on or off. Default is on."

View file

@ -251,11 +251,22 @@ function getSettings(): Settings {
settings.json!.schemas!.push(schemaSetting);
}
let fileMatches = setting.fileMatch;
let resultingFileMatches = schemaSetting.fileMatch!;
if (Array.isArray(fileMatches)) {
if (fileMatchPrefix) {
fileMatches = fileMatches.map(m => fileMatchPrefix + m);
for (let fileMatch of fileMatches) {
if (fileMatch[0] === '/') {
resultingFileMatches.push(fileMatchPrefix + fileMatch);
resultingFileMatches.push(fileMatchPrefix + '/*' + fileMatch);
} else {
resultingFileMatches.push(fileMatchPrefix + '/' + fileMatch);
resultingFileMatches.push(fileMatchPrefix + '/*/' + fileMatch);
}
}
} else {
resultingFileMatches.push(...fileMatches);
}
schemaSetting.fileMatch!.push(...fileMatches);
}
if (setting.schema) {
schemaSetting.schema = setting.schema;
@ -276,10 +287,10 @@ function getSettings(): Settings {
let folderSchemas = schemaConfigInfo!.workspaceFolderValue;
if (Array.isArray(folderSchemas)) {
let folderPath = folderUri.toString();
if (folderPath[folderPath.length - 1] !== '/') {
folderPath = folderPath + '/';
if (folderPath[folderPath.length - 1] === '/') {
folderPath = folderPath.substr(0, folderPath.length - 1);
}
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath + '*');
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath);
}
}
}

View file

@ -13,7 +13,7 @@
"dependencies": {
"jsonc-parser": "^2.0.0-next.1",
"request-light": "^0.2.2",
"vscode-json-languageservice": "^3.1.2-next.3",
"vscode-json-languageservice": "^3.1.2",
"vscode-languageserver": "^4.1.3",
"vscode-languageserver-protocol-foldingprovider": "^2.0.1",
"vscode-nls": "^3.2.2",

View file

@ -52,14 +52,14 @@ https-proxy-agent@2.1.1:
agent-base "^4.1.0"
debug "^3.1.0"
jsonc-parser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0.tgz#62ff087a7e753875febf3c55f1fc0cd737c36b5a"
jsonc-parser@^2.0.0-next.1:
version "2.0.0-next.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0-next.1.tgz#445a824f765a96abfbb286d759a9b1d226b18088"
jsonc-parser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -72,11 +72,11 @@ request-light@^0.2.2:
https-proxy-agent "2.1.1"
vscode-nls "^2.0.2"
vscode-json-languageservice@^3.1.2-next.3:
version "3.1.2-next.3"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.2-next.3.tgz#cc0902148f898b413987fb1b4c4a9e7fc1a79c78"
vscode-json-languageservice@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.2.tgz#5c70fc32ad389e6da48452e7b0187ea5e70f68bf"
dependencies:
jsonc-parser "^2.0.0"
jsonc-parser "^2.0.1"
vscode-languageserver-types "^3.7.2"
vscode-nls "^3.2.2"
vscode-uri "^1.0.3"

View file

@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* 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 updateGrammar = require('../../../build/npm/update-grammar');
function adaptJSON(grammar, replacementScope) {
grammar.name = 'JSON with comments';
grammar.scopeName = `source${replacementScope}`;
var fixScopeNames = function(rule) {
if (typeof rule.name === 'string') {
rule.name = rule.name.replace(/\.json/g, replacementScope);
}
if (typeof rule.contentName === 'string') {
rule.contentName = rule.contentName.replace(/\.json/g, replacementScope);
}
for (var property in rule) {
var value = rule[property];
if (typeof value === 'object') {
fixScopeNames(value);
}
}
};
var repository = grammar.repository;
for (var key in repository) {
fixScopeNames(repository[key]);
}
}
var tsGrammarRepo = 'Microsoft/vscode-JSON.tmLanguage';
updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSON.tmLanguage.json');
updateGrammar.update(tsGrammarRepo, 'JSON.tmLanguage', './syntaxes/JSONC.tmLanguage.json', grammar => adaptJSON(grammar, '.json.comments'));

View file

@ -8,7 +8,7 @@
"vscode": "0.10.x"
},
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-JSON.tmLanguage JSON.tmLanguage ./syntaxes/JSON.tmLanguage.json"
"update-grammar": "node ./build/update-grammars.js"
},
"contributes": {
"languages": [
@ -58,8 +58,8 @@
},
{
"language": "jsonc",
"scopeName": "source.json",
"path": "./syntaxes/JSON.tmLanguage.json"
"scopeName": "source.json.comments",
"path": "./syntaxes/JSONC.tmLanguage.json"
}
],
"jsonValidation": [

View file

@ -0,0 +1,213 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/Microsoft/vscode-JSON.tmLanguage/blob/master/JSON.tmLanguage",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70",
"name": "JSON with comments",
"scopeName": "source.json.comments",
"patterns": [
{
"include": "#value"
}
],
"repository": {
"array": {
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.definition.array.begin.json.comments"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.definition.array.end.json.comments"
}
},
"name": "meta.structure.array.json.comments",
"patterns": [
{
"include": "#value"
},
{
"match": ",",
"name": "punctuation.separator.array.json.comments"
},
{
"match": "[^\\s\\]]",
"name": "invalid.illegal.expected-array-separator.json.comments"
}
]
},
"comments": {
"patterns": [
{
"begin": "/\\*\\*(?!/)",
"captures": {
"0": {
"name": "punctuation.definition.comment.json.comments"
}
},
"end": "\\*/",
"name": "comment.block.documentation.json.comments"
},
{
"begin": "/\\*",
"captures": {
"0": {
"name": "punctuation.definition.comment.json.comments"
}
},
"end": "\\*/",
"name": "comment.block.json.comments"
},
{
"captures": {
"1": {
"name": "punctuation.definition.comment.json.comments"
}
},
"match": "(//).*$\\n?",
"name": "comment.line.double-slash.js"
}
]
},
"constant": {
"match": "\\b(?:true|false|null)\\b",
"name": "constant.language.json.comments"
},
"number": {
"match": "(?x) # turn on extended mode\n -? # an optional minus\n (?:\n 0 # a zero\n | # ...or...\n [1-9] # a 1-9 character\n \\d* # followed by zero or more digits\n )\n (?:\n (?:\n \\. # a period\n \\d+ # followed by one or more digits\n )?\n (?:\n [eE] # an e character\n [+-]? # followed by an option +/-\n \\d+ # followed by one or more digits\n )? # make exponent optional\n )? # make decimal portion optional",
"name": "constant.numeric.json.comments"
},
"object": {
"begin": "\\{",
"beginCaptures": {
"0": {
"name": "punctuation.definition.dictionary.begin.json.comments"
}
},
"end": "\\}",
"endCaptures": {
"0": {
"name": "punctuation.definition.dictionary.end.json.comments"
}
},
"name": "meta.structure.dictionary.json.comments",
"patterns": [
{
"comment": "the JSON object key",
"include": "#objectkey"
},
{
"include": "#comments"
},
{
"begin": ":",
"beginCaptures": {
"0": {
"name": "punctuation.separator.dictionary.key-value.json.comments"
}
},
"end": "(,)|(?=\\})",
"endCaptures": {
"1": {
"name": "punctuation.separator.dictionary.pair.json.comments"
}
},
"name": "meta.structure.dictionary.value.json.comments",
"patterns": [
{
"comment": "the JSON object value",
"include": "#value"
},
{
"match": "[^\\s,]",
"name": "invalid.illegal.expected-dictionary-separator.json.comments"
}
]
},
{
"match": "[^\\s\\}]",
"name": "invalid.illegal.expected-dictionary-separator.json.comments"
}
]
},
"string": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.json.comments"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.json.comments"
}
},
"name": "string.quoted.double.json.comments",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"objectkey": {
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.support.type.property-name.begin.json.comments"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.support.type.property-name.end.json.comments"
}
},
"name": "string.json.comments support.type.property-name.json.comments",
"patterns": [
{
"include": "#stringcontent"
}
]
},
"stringcontent": {
"patterns": [
{
"match": "(?x) # turn on extended mode\n \\\\ # a literal backslash\n (?: # ...followed by...\n [\"\\\\/bfnrt] # one of these characters\n | # ...or...\n u # a u\n [0-9a-fA-F]{4}) # and four hex digits",
"name": "constant.character.escape.json.comments"
},
{
"match": "\\\\.",
"name": "invalid.illegal.unrecognized-string-escape.json.comments"
}
]
},
"value": {
"patterns": [
{
"include": "#constant"
},
{
"include": "#number"
},
{
"include": "#string"
},
{
"include": "#array"
},
{
"include": "#object"
},
{
"include": "#comments"
}
]
}
}
}

View file

@ -120,6 +120,11 @@ Visual Studio-like style based on original C# coloring by Jason Diamond <jason@d
*/
.vscode-light .hljs-function,
.vscode-light .hljs-params {
color: inherit;
}
.vscode-light .hljs-comment,
.vscode-light .hljs-quote,
.vscode-light .hljs-variable {

View file

@ -196,18 +196,15 @@ pre code {
}
.vscode-light pre:not(.hljs),
.vscode-light code > div {
.vscode-light pre {
background-color: rgba(220, 220, 220, 0.4);
}
.vscode-dark pre:not(.hljs),
.vscode-dark code > div {
.vscode-dark pre {
background-color: rgba(10, 10, 10, 0.4);
}
.vscode-high-contrast pre:not(.hljs),
.vscode-high-contrast code > div {
.vscode-high-contrast pre {
background-color: rgb(0, 0, 0);
}

View file

@ -5,8 +5,13 @@
import * as vscode from 'vscode';
import { MarkdownEngine } from '../markdownEngine';
import { TableOfContentsProvider, SkinnyTextDocument } from '../tableOfContentsProvider';
import { TableOfContentsProvider, SkinnyTextDocument, TocEntry } from '../tableOfContentsProvider';
interface MarkdownSymbol {
readonly level: number;
readonly parent: MarkdownSymbol | undefined;
readonly children: vscode.DocumentSymbol[];
}
export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
@ -14,10 +19,57 @@ export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolPr
private readonly engine: MarkdownEngine
) { }
public async provideDocumentSymbols(document: SkinnyTextDocument): Promise<vscode.SymbolInformation[]> {
public async provideDocumentSymbolInformation(document: SkinnyTextDocument): Promise<vscode.SymbolInformation[]> {
const toc = await new TableOfContentsProvider(this.engine, document).getToc();
return toc.map(entry => {
return new vscode.SymbolInformation('#'.repeat(entry.level) + ' ' + entry.text, vscode.SymbolKind.String, '', entry.location);
});
return toc.map(entry => this.toSymbolInformation(entry));
}
public async provideDocumentSymbols(document: SkinnyTextDocument): Promise<vscode.DocumentSymbol[]> {
const toc = await new TableOfContentsProvider(this.engine, document).getToc();
const root: MarkdownSymbol = {
level: -Infinity,
children: [],
parent: undefined
};
this.buildTree(root, toc);
return root.children;
}
private buildTree(parent: MarkdownSymbol, entries: TocEntry[]) {
if (!entries.length) {
return;
}
const entry = entries[0];
const symbol = this.toDocumentSymbol(entry);
symbol.children = [];
while (parent && entry.level <= parent.level) {
parent = parent.parent!;
}
parent.children.push(symbol);
this.buildTree({ level: entry.level, children: symbol.children, parent }, entries.slice(1));
}
private toSymbolInformation(entry: TocEntry): vscode.SymbolInformation {
return new vscode.SymbolInformation(
this.getSymbolName(entry),
vscode.SymbolKind.String,
'',
entry.location);
}
private toDocumentSymbol(entry: TocEntry) {
return new vscode.DocumentSymbol(
this.getSymbolName(entry),
'',
vscode.SymbolKind.String,
entry.location.range,
entry.location.range);
}
private getSymbolName(entry: TocEntry): string {
return '#'.repeat(entry.level) + ' ' + entry.text;
}
}

View file

@ -136,7 +136,7 @@ export default class MarkdownWorkspaceSymbolProvider implements vscode.Workspace
private getSymbols(document: SkinnyTextDocument): Lazy<Thenable<vscode.SymbolInformation[]>> {
return lazy(async () => {
return this._symbolProvider.provideDocumentSymbols(document);
return this._symbolProvider.provideDocumentSymbolInformation(document);
});
}

View file

@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* 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 'mocha';
import * as vscode from 'vscode';
import SymbolProvider from '../features/documentSymbolProvider';
import { InMemoryDocument } from './inMemoryDocument';
import { createNewMarkdownEngine } from './engine';
const testFileName = vscode.Uri.parse('test.md');
function getSymbolsForFile(fileContents: string) {
const doc = new InMemoryDocument(testFileName, fileContents);
const provider = new SymbolProvider(createNewMarkdownEngine());
return provider.provideDocumentSymbols(doc);
}
suite('markdown.DocumentSymbolProvider', () => {
test('Should not return anything for empty document', async () => {
const symbols = await getSymbolsForFile('');
assert.strictEqual(symbols.length, 0);
});
test('Should not return anything for document with no headers', async () => {
const symbols = await getSymbolsForFile('a\na');
assert.strictEqual(symbols.length, 0);
});
test('Should not return anything for document with # but no real headers', async () => {
const symbols = await getSymbolsForFile('a#a\na#');
assert.strictEqual(symbols.length, 0);
});
test('Should return single symbol for single header', async () => {
const symbols = await getSymbolsForFile('# h');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
});
test('Should not care about symbol level for single header', async () => {
const symbols = await getSymbolsForFile('### h');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '### h');
});
test('Should put symbols of same level in flat list', async () => {
const symbols = await getSymbolsForFile('## h\n## h2');
assert.strictEqual(symbols.length, 2);
assert.strictEqual(symbols[0].name, '## h');
assert.strictEqual(symbols[1].name, '## h2');
});
test('Should nest symbol of level - 1 under parent', async () => {
const symbols = await getSymbolsForFile('# h\n## h2\n## h3');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 2);
assert.strictEqual(symbols[0].children[0].name, '## h2');
assert.strictEqual(symbols[0].children[1].name, '## h3');
});
test('Should nest symbol of level - n under parent', async () => {
const symbols = await getSymbolsForFile('# h\n#### h2');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 1);
assert.strictEqual(symbols[0].children[0].name, '#### h2');
});
test('Should flatten children where lower level occurs first', async () => {
const symbols = await getSymbolsForFile('# h\n### h2\n## h3');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 2);
assert.strictEqual(symbols[0].children[0].name, '### h2');
assert.strictEqual(symbols[0].children[1].name, '## h3');
});
});

View file

@ -1,6 +1,6 @@
{
"description": "Extension to add task support for npm scripts.",
"displayName": "Npm support for VSCode",
"displayName": "Npm support for VS Code",
"config.npm.autoDetect": "Controls whether auto detection of npm scripts is on or off. Default is on.",
"config.npm.runSilent": "Run npm commands with the `--silent` option.",
"config.npm.packageManager": "The package manager used to run scripts.",

View file

@ -290,7 +290,9 @@
}
},
{
"scope": "support.type.property-name.json",
"scope": [
"support.type.property-name.json"
],
"settings": {
"foreground": "#0451a5"
}

View file

@ -40,7 +40,8 @@
"name": "Comments: Documentation",
"scope": [
"comment.documentation",
"comment.block.documentation"
"comment.block.documentation",
"comment.block.documentation punctuation.definition.comment "
],
"settings": {
"foreground": "#448C27"

View file

@ -1,7 +1,7 @@
[
{
"c": "{",
"t": "source.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json",
"t": "source.json.comments meta.structure.dictionary.json.comments punctuation.definition.dictionary.begin.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -12,7 +12,7 @@
},
{
"c": "\t",
"t": "source.json meta.structure.dictionary.json",
"t": "source.json.comments meta.structure.dictionary.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -23,7 +23,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json",
"t": "source.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments punctuation.support.type.property-name.begin.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -34,7 +34,7 @@
},
{
"c": "compilerOptions",
"t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json",
"t": "source.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -45,7 +45,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json",
"t": "source.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments punctuation.support.type.property-name.end.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -56,7 +56,7 @@
},
{
"c": ":",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments punctuation.separator.dictionary.key-value.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -67,7 +67,7 @@
},
{
"c": " ",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -78,7 +78,7 @@
},
{
"c": "{",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.begin.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments punctuation.definition.dictionary.begin.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -89,7 +89,7 @@
},
{
"c": "\t\t",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -100,7 +100,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.begin.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments punctuation.support.type.property-name.begin.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -111,7 +111,7 @@
},
{
"c": "target",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -122,7 +122,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.json support.type.property-name.json punctuation.support.type.property-name.end.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments string.json.comments support.type.property-name.json.comments punctuation.support.type.property-name.end.json.comments",
"r": {
"dark_plus": "support.type.property-name: #9CDCFE",
"light_plus": "support.type.property-name.json: #0451A5",
@ -133,7 +133,7 @@
},
{
"c": ":",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json punctuation.separator.dictionary.key-value.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments punctuation.separator.dictionary.key-value.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -144,7 +144,7 @@
},
{
"c": " ",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -155,7 +155,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.begin.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments string.quoted.double.json.comments punctuation.definition.string.begin.json.comments",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
@ -166,7 +166,7 @@
},
{
"c": "es6",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments string.quoted.double.json.comments",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
@ -177,7 +177,7 @@
},
{
"c": "\"",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json punctuation.definition.string.end.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments string.quoted.double.json.comments punctuation.definition.string.end.json.comments",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
@ -188,7 +188,7 @@
},
{
"c": "\t",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -199,7 +199,7 @@
},
{
"c": "}",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json",
"t": "source.json.comments meta.structure.dictionary.json.comments meta.structure.dictionary.value.json.comments meta.structure.dictionary.json.comments punctuation.definition.dictionary.end.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@ -210,7 +210,7 @@
},
{
"c": "}",
"t": "source.json meta.structure.dictionary.json punctuation.definition.dictionary.end.json",
"t": "source.json.comments meta.structure.dictionary.json.comments punctuation.definition.dictionary.end.json.comments",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",

View file

@ -46,7 +46,7 @@
"typescript.problemMatchers.tsc.label": "TypeScript problems",
"typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)",
"typescript.quickSuggestionsForPaths": "Enable/disable quick suggestions when typing out an import path.",
"typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0. Default of 'null' uses VS Code's locale. or newer in the workspace.",
"typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of 'null' uses VS Code's locale.",
"javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable 'experimentalDecorators' for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.",
"typescript.autoImportSuggestions.enabled": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.",
"taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.",
@ -56,4 +56,4 @@
"typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports:\n- \"relative\" to the file location.\n- \"non-relative\" based on the 'baseUrl' configured in your 'jsconfig.json' / 'tsconfig.json'.\n- \"auto\" infer the shortest path type.\nRequires using TypeScript 2.9 or newer in the workspace.",
"typescript.showUnused": "Enable/disable highlighting of unused variables in code. Requires using TypeScript 2.9 or newer in the workspace.",
"typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace."
}
}

View file

@ -225,6 +225,16 @@ export class UpdateImportsOnFileRenameHandler {
const edits: Proto.FileCodeEdits[] = [];
for (const edit of response.body) {
// Workaround for https://github.com/Microsoft/vscode/issues/52675
if ((edit as Proto.FileCodeEdits).fileName.match(/[\/\\]node_modules[\/\\]/gi)) {
continue;
}
for (const change of (edit as Proto.FileCodeEdits).textChanges) {
if (change.newText.match(/\/node_modules\//gi)) {
continue;
}
}
edits.push(await this.fixEdit(edit, isDirectoryRename, oldFile, newFile));
}
return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, edits);

View file

@ -13,7 +13,10 @@ interface QuickPickExpected {
events: string[];
activeItems: string[][];
selectionItems: string[][];
acceptedItems: string[][];
acceptedItems: {
active: string[][];
selection: string[][];
};
}
suite('window namespace tests', function () {
@ -31,8 +34,11 @@ suite('window namespace tests', function () {
events: ['active', 'active', 'selection', 'accept', 'hide'],
activeItems: [['eins'], ['zwei']],
selectionItems: [['zwei']],
acceptedItems: [['zwei']],
}, done);
acceptedItems: {
active: [['zwei']],
selection: [['zwei']]
},
}, (err?: any) => done(err));
quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label }));
quickPick.show();
@ -53,8 +59,11 @@ suite('window namespace tests', function () {
events: ['active', 'selection', 'accept', 'hide'],
activeItems: [['zwei']],
selectionItems: [['zwei']],
acceptedItems: [['zwei']],
}, done);
acceptedItems: {
active: [['zwei']],
selection: [['zwei']]
},
}, (err?: any) => done(err));
quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label }));
quickPick.activeItems = [quickPick.items[1]];
quickPick.show();
@ -64,6 +73,35 @@ suite('window namespace tests', function () {
})()
.catch(err => done(err));
});
test('createQuickPick, select first and second', function (_done) {
let done = (err?: any) => {
done = () => {};
_done(err);
};
const quickPick = createQuickPick({
events: ['active', 'selection', 'active', 'selection', 'accept', 'hide'],
activeItems: [['eins'], ['zwei']],
selectionItems: [['eins'], ['eins', 'zwei']],
acceptedItems: {
active: [['zwei']],
selection: [['eins', 'zwei']]
},
}, (err?: any) => done(err));
quickPick.canSelectMany = true;
quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label }));
quickPick.show();
(async () => {
await commands.executeCommand('workbench.action.quickOpenSelectNext');
await commands.executeCommand('workbench.action.quickPickManyToggle');
await commands.executeCommand('workbench.action.quickOpenSelectNext');
await commands.executeCommand('workbench.action.quickPickManyToggle');
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
})()
.catch(err => done(err));
});
});
});
@ -92,9 +130,10 @@ function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void)
quickPick.onDidAccept(() => {
try {
assert.equal('accept', expected.events.shift());
const expectedItems = expected.acceptedItems.shift();
assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedItems);
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedItems);
const expectedActive = expected.acceptedItems.active.shift();
assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedActive);
const expectedSelection = expected.acceptedItems.selection.shift();
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedSelection);
quickPick.dispose();
} catch (err) {
done(err);

View file

@ -410,9 +410,14 @@ suite('window namespace tests', () => {
test('showQuickPick, accept second', async function () {
const resolves: ((value: string) => void)[] = [];
let done: () => void;
const unexpected = new Promise((resolve, reject) => {
done = () => resolve();
resolves.push(reject);
});
const first = new Promise(resolve => resolves.push(resolve));
const pick = window.showQuickPick(['eins', 'zwei', 'drei'], {
onDidSelectItem: item => resolves.shift()!(item as string)
onDidSelectItem: item => resolves.pop()!(item as string)
});
assert.equal(await first, 'eins');
const second = new Promise(resolve => resolves.push(resolve));
@ -420,12 +425,19 @@ suite('window namespace tests', () => {
assert.equal(await second, 'zwei');
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
assert.equal(await pick, 'zwei');
done!();
return unexpected;
});
test('showQuickPick, select first two', async function () {
const resolves: ((value: string) => void)[] = [];
let done: () => void;
const unexpected = new Promise((resolve, reject) => {
done = () => resolve();
resolves.push(reject);
});
const picks = window.showQuickPick(['eins', 'zwei', 'drei'], {
onDidSelectItem: item => resolves.shift()!(item as string),
onDidSelectItem: item => resolves.pop()!(item as string),
canPickMany: true
});
const first = new Promise(resolve => resolves.push(resolve));
@ -438,6 +450,8 @@ suite('window namespace tests', () => {
await commands.executeCommand('workbench.action.quickPickManyToggle');
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
assert.deepStrictEqual(await picks, ['eins', 'zwei']);
done!();
return unexpected;
});
test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () {
@ -646,7 +660,7 @@ suite('window namespace tests', () => {
});
const renderer = window.createTerminalRenderer('foo');
const reg2 = renderer.onDidChangeMaximumDimensions(dimensions => {
assert.ok(dimensions.cols > 0);
assert.ok(dimensions.columns > 0);
assert.ok(dimensions.rows > 0);
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {
@ -660,7 +674,7 @@ suite('window namespace tests', () => {
test('TerminalRenderer.write should fire Terminal.onData', (done) => {
const reg1 = window.onDidOpenTerminal(terminal => {
reg1.dispose();
const reg2 = terminal.onData(data => {
const reg2 = terminal.onDidWriteData(data => {
assert.equal(data, 'bar');
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {
@ -677,7 +691,7 @@ suite('window namespace tests', () => {
test('Terminal.sendText should fire Termnial.onInput', (done) => {
const reg1 = window.onDidOpenTerminal(terminal => {
reg1.dispose();
const reg2 = renderer.onInput(data => {
const reg2 = renderer.onDidAcceptInput(data => {
assert.equal(data, 'bar');
reg2.dispose();
const reg3 = window.onDidCloseTerminal(() => {

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.25.0",
"distro": "974512091c88e4e435fc2a427cf44b08e61a57dd",
"distro": "a1c7a95e4b1313b9d6a98aa45b7c406731404e31",
"author": {
"name": "Microsoft Corporation"
},
@ -11,11 +11,11 @@
"test": "mocha",
"preinstall": "node build/npm/preinstall.js",
"postinstall": "node build/npm/postinstall.js",
"compile": "gulp compile --max_old_space_size=4096",
"watch": "gulp watch --max_old_space_size=4096",
"compile": "gulp compile --max_old_space_size=4095",
"watch": "gulp watch --max_old_space_size=4095",
"monaco-editor-test": "mocha --only-monaco-editor",
"precommit": "node build/gulpfile.hygiene.js",
"gulp": "gulp --max_old_space_size=4096",
"gulp": "gulp --max_old_space_size=4095",
"7z": "7z",
"update-grammars": "node build/npm/update-all-grammars.js",
"update-localization-extension": "node build/npm/update-localization-extension.js",
@ -39,13 +39,13 @@
"native-is-elevated": "^0.2.1",
"native-keymap": "1.2.5",
"native-watchdog": "0.3.0",
"node-pty": "0.7.5",
"node-pty": "0.7.6",
"semver": "^5.5.0",
"spdlog": "0.6.0",
"sudo-prompt": "8.2.0",
"v8-inspect-profiler": "^0.0.8",
"vscode-chokidar": "1.6.2",
"vscode-debugprotocol": "1.28.0",
"vscode-debugprotocol": "1.30.0",
"vscode-nsfw": "1.0.17",
"vscode-ripgrep": "^1.0.1",
"vscode-textmate": "^4.0.1",
@ -137,4 +137,4 @@
"windows-mutex": "^0.2.0",
"windows-process-tree": "0.2.2"
}
}
}

View file

@ -20,8 +20,20 @@ function getApplicationPath() {
}
}
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
const portableDataPath = path.join(path.dirname(getApplicationPath()), portableDataName);
function getPortableDataPath() {
if (process.env['VSCODE_PORTABLE']) {
return process.env['VSCODE_PORTABLE'];
}
if (process.platform === 'win32' || process.platform === 'linux') {
return path.join(getApplicationPath(), 'data');
} else {
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
return path.join(path.dirname(getApplicationPath()), portableDataName);
}
}
const portableDataPath = getPortableDataPath();
const isPortable = fs.existsSync(portableDataPath);
const portableTempPath = path.join(portableDataPath, 'tmp');
const isTempPortable = isPortable && fs.existsSync(portableTempPath);

View file

@ -27,8 +27,20 @@ function getApplicationPath() {
}
}
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
const portableDataPath = process.env['VSCODE_PORTABLE'] || path.join(path.dirname(getApplicationPath()), portableDataName);
function getPortableDataPath() {
if (process.env['VSCODE_PORTABLE']) {
return process.env['VSCODE_PORTABLE'];
}
if (process.platform === 'win32' || process.platform === 'linux') {
return path.join(getApplicationPath(), 'data');
} else {
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
return path.join(path.dirname(getApplicationPath()), portableDataName);
}
}
const portableDataPath = getPortableDataPath();
const isPortable = fs.existsSync(portableDataPath);
const portableTempPath = path.join(portableDataPath, 'tmp');
const isTempPortable = isPortable && fs.existsSync(portableTempPath);

View file

@ -4,10 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IAction, IActionRunner, Action } 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';
import { SubmenuAction } from 'vs/base/browser/ui/menu/menu';
export interface IEvent {
shiftKey?: boolean;
@ -16,9 +17,9 @@ export interface IEvent {
metaKey?: boolean;
}
export class ContextSubMenu extends Action {
export class ContextSubMenu extends SubmenuAction {
constructor(label: string, public entries: (ContextSubMenu | IAction)[]) {
super('contextsubmenu', label, '', true);
super(label, entries, 'contextsubmenu');
}
}

View file

@ -819,6 +819,7 @@ export const EventType = {
MOUSE_OVER: 'mouseover',
MOUSE_MOVE: 'mousemove',
MOUSE_OUT: 'mouseout',
MOUSE_LEAVE: 'mouseleave',
CONTEXT_MENU: 'contextmenu',
WHEEL: 'wheel',
// Keyboard

View file

@ -54,7 +54,7 @@
.monaco-action-bar .action-label {
font-size: 11px;
margin-right: 4px;
margin-right: 4px;
}
.monaco-action-bar .action-label.octicon {

View file

@ -43,8 +43,6 @@ export class BaseActionItem implements IActionItem {
public _context: any;
public _action: IAction;
static MNEMONIC_REGEX: RegExp = /&&(.)/g;
private _actionRunner: IActionRunner;
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
@ -156,7 +154,7 @@ export class BaseActionItem implements IActionItem {
DOM.EventHelper.stop(event, true);
let context: any;
if (types.isUndefinedOrNull(this._context)) {
if (types.isUndefinedOrNull(this._context) || !types.isObject(this._context)) {
context = event;
} else {
context = this._context;
@ -277,11 +275,7 @@ export class ActionItem extends BaseActionItem {
public _updateLabel(): void {
if (this.options.label) {
let label = this.getAction().label;
if (label && this.options.isMenu) {
label = label.replace(BaseActionItem.MNEMONIC_REGEX, '$1\u0332');
}
this.$e.text(label);
this.$e.text(this.getAction().label);
}
}
@ -565,15 +559,6 @@ export class ActionBar implements IActionRunner {
return this.domNode;
}
private _addMnemonic(action: IAction, actionItemElement: HTMLElement): void {
let matches = BaseActionItem.MNEMONIC_REGEX.exec(action.label);
if (matches && matches.length === 2) {
let mnemonic = matches[1];
actionItemElement.accessKey = mnemonic.toLocaleLowerCase();
}
}
public push(arg: IAction | IAction[], options: IActionOptions = {}): void {
const actions: IAction[] = !Array.isArray(arg) ? [arg] : arg;
@ -591,10 +576,6 @@ export class ActionBar implements IActionRunner {
e.stopPropagation();
});
if (options.isMenu) {
this._addMnemonic(action, actionItemElement);
}
let item: IActionItem = null;
if (this.options.actionItemProvider) {
@ -659,10 +640,12 @@ export class ActionBar implements IActionRunner {
public focus(selectFirst?: boolean): void {
if (selectFirst && typeof this.focusedItem === 'undefined') {
this.focusedItem = 0;
// Focus the first enabled item
this.focusedItem = this.items.length - 1;
this.focusNext();
} else {
this.updateFocus();
}
this.updateFocus();
}
private focusNext(): void {

View file

@ -60,8 +60,15 @@ export class CenteredViewLayout {
constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) {
this.container.appendChild(this.view.element);
// Make sure to hide the split view overflow like sashes #52892
this.container.style.overflow = 'hidden';
}
get minimumWidth(): number { return this.splitView ? this.splitView.minimumSize : this.view.minimumWidth; }
get maximumWidth(): number { return this.splitView ? this.splitView.maximumSize : this.view.maximumWidth; }
get minimumHeight(): number { return this.view.minimumHeight; }
get maximumHeight(): number { return this.view.maximumHeight; }
layout(width: number, height: number): void {
this.width = width;
this.height = height;

View file

@ -513,9 +513,9 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge
this.history = new HistoryNavigator<string>(options.history, 100);
}
public addToHistory(value: string): void {
if (value !== this.history.current()) {
this.history.add(value);
public addToHistory(): void {
if (this.value && this.value !== this.getCurrentValue()) {
this.history.add(this.value);
}
}
@ -524,17 +524,26 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge
}
public showNextValue(): void {
let next = this.history.next();
let next = this.getNextValue();
if (next) {
next = next === this.value ? this.getNextValue() : next;
}
if (next) {
this.value = next;
}
}
public showPreviousValue(): void {
if (this.value.length !== 0) {
this.history.addIfNotPresent(this.value);
if (!this.history.has(this.value)) {
this.addToHistory();
}
const previous = this.history.previous();
let previous = this.getPreviousValue();
if (previous) {
previous = previous === this.value ? this.getPreviousValue() : previous;
}
if (previous) {
this.value = previous;
}
@ -543,4 +552,21 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge
public clearHistory(): void {
this.history.clear();
}
private getCurrentValue(): string {
let currentValue = this.history.current();
if (!currentValue) {
currentValue = this.history.last();
this.history.next();
}
return currentValue;
}
private getPreviousValue(): string {
return this.history.previous() || this.history.first();
}
private getNextValue(): string {
return this.history.next() || this.history.last();
}
}

View file

@ -5,6 +5,7 @@
.monaco-menu .monaco-action-bar.vertical {
margin-left: 0;
overflow: visible;
}
.monaco-menu .monaco-action-bar.vertical .actions-container {
@ -47,7 +48,8 @@
background: none;
}
.monaco-menu .monaco-action-bar.vertical .keybinding {
.monaco-menu .monaco-action-bar.vertical .keybinding,
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
display: inline-block;
-ms-flex: 2 1 auto;
flex: 2 1 auto;
@ -57,7 +59,12 @@
text-align: right;
}
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding {
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
padding: 0.8em .5em;
}
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator {
opacity: 0.4;
}

View file

@ -7,11 +7,13 @@
import 'vs/css!./menu';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IActionRunner, IAction } from 'vs/base/common/actions';
import { ActionBar, IActionItemProvider, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IActionRunner, IAction, Action } from 'vs/base/common/actions';
import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { Event } from 'vs/base/common/event';
import { addClass } from 'vs/base/browser/dom';
import { addClass, EventType, EventHelper, EventLike } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { $ } from 'vs/base/browser/builder';
export interface IMenuOptions {
context?: any;
@ -21,6 +23,18 @@ export interface IMenuOptions {
ariaLabel?: string;
}
export class SubmenuAction extends Action {
constructor(label: string, public entries: (SubmenuAction | IAction)[], cssClass?: string) {
super(!!cssClass ? cssClass : 'submenu', label, '', true);
}
}
interface ISubMenuData {
parent: Menu;
submenu?: Menu;
}
export class Menu {
private actionBar: ActionBar;
@ -35,9 +49,13 @@ export class Menu {
menuContainer.setAttribute('role', 'presentation');
container.appendChild(menuContainer);
let parentData: ISubMenuData = {
parent: this
};
this.actionBar = new ActionBar(menuContainer, {
orientation: ActionsOrientation.VERTICAL,
actionItemProvider: options.actionItemProvider,
actionItemProvider: action => this.doGetActionItem(action, options, parentData),
context: options.context,
actionRunner: options.actionRunner,
isMenu: true,
@ -47,6 +65,24 @@ export class Menu {
this.actionBar.push(actions, { icon: true, label: true, isMenu: true });
}
private doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): ActionItem {
if (action instanceof Separator) {
return new ActionItem(options.context, action, { icon: true });
} else if (action instanceof SubmenuAction) {
return new SubmenuActionItem(action, action.entries, parentData, options);
} else {
const menuItemOptions: IActionItemOptions = {};
if (options.getKeyBinding) {
const keybinding = options.getKeyBinding(action);
if (keybinding) {
menuItemOptions.keybinding = keybinding.getLabel();
}
}
return new MenuActionItem(options.context, action, menuItemOptions);
}
}
public get onDidCancel(): Event<void> {
return this.actionBar.onDidCancel;
}
@ -56,7 +92,9 @@ export class Menu {
}
public focus() {
this.actionBar.focus(true);
if (this.actionBar) {
this.actionBar.focus(true);
}
}
public dispose() {
@ -70,4 +108,156 @@ export class Menu {
this.listener = null;
}
}
}
class MenuActionItem extends ActionItem {
static MNEMONIC_REGEX: RegExp = /&&(.)/g;
constructor(ctx: any, action: IAction, options: IActionItemOptions = {}) {
options.isMenu = true;
super(action, action, options);
}
private _addMnemonic(action: IAction, actionItemElement: HTMLElement): void {
let matches = MenuActionItem.MNEMONIC_REGEX.exec(action.label);
if (matches && matches.length === 2) {
let mnemonic = matches[1];
let ariaLabel = action.label.replace(MenuActionItem.MNEMONIC_REGEX, mnemonic);
actionItemElement.accessKey = mnemonic.toLocaleLowerCase();
this.$e.attr('aria-label', ariaLabel);
} else {
this.$e.attr('aria-label', action.label);
}
}
public render(container: HTMLElement): void {
super.render(container);
this._addMnemonic(this.getAction(), container);
this.$e.attr('role', 'menuitem');
}
public _updateLabel(): void {
if (this.options.label) {
let label = this.getAction().label;
if (label && this.options.isMenu) {
label = label.replace(MenuActionItem.MNEMONIC_REGEX, '$1\u0332');
}
this.$e.text(label);
}
}
}
class SubmenuActionItem extends MenuActionItem {
private mysubmenu: Menu;
private mouseOver: boolean;
constructor(
action: IAction,
private submenuActions: IAction[],
private parentData: ISubMenuData,
private submenuOptions?: IMenuOptions
) {
super(action, action, { label: true, isMenu: true });
}
public render(container: HTMLElement): void {
super.render(container);
this.builder = $(container);
$(this.builder).addClass('monaco-submenu-item');
$('span.submenu-indicator').text('\u25B6').appendTo(this.builder);
this.$e.attr('role', 'menu');
$(this.builder).on(EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.RightArrow)) {
EventHelper.stop(e, true);
this.createSubmenu();
}
});
$(this.builder).on(EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.RightArrow)) {
EventHelper.stop(e, true);
}
});
$(this.builder).on(EventType.MOUSE_OVER, (e) => {
if (!this.mouseOver) {
this.mouseOver = true;
setTimeout(() => {
if (this.mouseOver) {
this.cleanupExistingSubmenu(false);
this.createSubmenu();
}
}, 250);
}
});
$(this.builder).on(EventType.MOUSE_LEAVE, (e) => {
this.mouseOver = false;
setTimeout(() => {
if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) {
this.parentData.parent.focus();
this.cleanupExistingSubmenu(true);
}
}, 750);
});
}
public onClick(e: EventLike) {
// stop clicking from trying to run an action
EventHelper.stop(e, true);
}
private cleanupExistingSubmenu(force: boolean) {
if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) {
this.parentData.submenu.dispose();
this.parentData.submenu = null;
}
}
private createSubmenu() {
if (!this.parentData.submenu) {
const submenuContainer = $(this.builder).div({ class: 'monaco-submenu menubar-menu-items-holder context-view' });
$(submenuContainer).style({
'left': `${$(this.builder).getClientArea().width}px`
});
$(submenuContainer).on(EventType.KEY_UP, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.LeftArrow)) {
EventHelper.stop(e, true);
this.parentData.parent.focus();
this.parentData.submenu.dispose();
this.parentData.submenu = null;
}
});
$(submenuContainer).on(EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e as KeyboardEvent);
if (event.equals(KeyCode.LeftArrow)) {
EventHelper.stop(e, true);
}
});
this.parentData.submenu = new Menu(submenuContainer.getHTMLElement(), this.submenuActions, this.submenuOptions);
this.parentData.submenu.focus();
this.mysubmenu = this.parentData.submenu;
}
}
}

View file

@ -27,12 +27,6 @@ export class HistoryNavigator<T> implements INavigator<T> {
this._onChange();
}
public addIfNotPresent(t: T) {
if (!this._history.has(t)) {
this.add(t);
}
}
public next(): T {
return this._navigator.next();
}
@ -57,6 +51,10 @@ export class HistoryNavigator<T> implements INavigator<T> {
return this._navigator.last();
}
public has(t: T): boolean {
return this._history.has(t);
}
public clear(): void {
this._initialize([]);
this._onChange();

View file

@ -407,6 +407,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON
case CharacterCodes.doubleQuote:
case CharacterCodes.colon:
case CharacterCodes.comma:
case CharacterCodes.slash:
return false;
}
return true;
@ -628,7 +629,7 @@ export type JSONPath = Segment[];
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
disallowTrailingComma?: boolean;
}
/**
@ -817,7 +818,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
onError = toOneArgVisit(visitor.onError);
let disallowComments = options && options.disallowComments;
let allowTrailingComma = options && options.allowTrailingComma;
let disallowTrailingComma = options && options.disallowTrailingComma;
function scanNext(): SyntaxKind {
while (true) {
let token = _scanner.scan();
@ -929,7 +930,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === SyntaxKind.CloseBraceToken && allowTrailingComma) {
if (_scanner.getToken() === SyntaxKind.CloseBraceToken && !disallowTrailingComma) {
break;
}
} else if (needsComma) {
@ -961,7 +962,7 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === SyntaxKind.CloseBracketToken && allowTrailingComma) {
if (_scanner.getToken() === SyntaxKind.CloseBracketToken && !disallowTrailingComma) {
break;
}
} else if (needsComma) {

View file

@ -225,6 +225,7 @@ export function getOrDefault<T, R>(obj: T, fn: (obj: T) => R, defaultValue: R =
return typeof result === 'undefined' ? defaultValue : result;
}
type obj = { [key: string]: any; };
/**
* Returns an object that has keys for each value that is different in the base object. Keys
* that do not exist in the target but in the base object are not considered.
@ -235,7 +236,6 @@ export function getOrDefault<T, R>(obj: T, fn: (obj: T) => R, defaultValue: R =
* @param base the object to diff against
* @param obj the object to use for diffing
*/
export type obj = { [key: string]: any; };
export function distinct(base: obj, target: obj): obj {
const result = Object.create(null);

View file

@ -114,7 +114,6 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log
}, e));
zipfile.readEntry();
zipfile.on('entry', (entry: Entry) => {
logService.debug(targetPath, 'Found', entry.fileName);
if (isCanceled) {
return;

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChildProcess, fork } from 'child_process';
import { ChildProcess, fork, ForkOptions } from 'child_process';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { Delayer } from 'vs/base/common/async';
@ -125,7 +125,7 @@ export class Client implements IChannelClient, IDisposable {
private get client(): IPCClient {
if (!this._client) {
const args = this.options && this.options.args ? this.options.args : [];
const forkOpts = Object.create(null);
const forkOpts: ForkOptions = Object.create(null);
forkOpts.env = assign(deepClone(process.env), { 'VSCODE_PARENT_PID': String(process.pid) });
@ -183,7 +183,7 @@ export class Client implements IChannelClient, IDisposable {
}
if (code !== 0 && signal !== 'SIGTERM') {
console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code);
console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code + ' and signal ' + signal);
this.disposeDelayer.cancel();
this.disposeClient();
}

View file

@ -43,9 +43,9 @@ function assertInvalidParse(input: string, expected: any, options?: ParseOptions
assert.deepEqual(actual, expected);
}
function assertTree(input: string, expected: any, expectedErrors: number[] = []): void {
function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void {
var errors: ParseError[] = [];
var actual = parseTree(input, errors);
var actual = parseTree(input, errors, options);
assert.deepEqual(errors.map(e => e.error, expected), expectedErrors);
let checkParent = (node: Node) => {
@ -203,7 +203,7 @@ suite('JSON', () => {
test('parse: objects with errors', () => {
assertInvalidParse('{,}', {});
assertInvalidParse('{ "foo": true, }', { foo: true });
assertInvalidParse('{ "foo": true, }', { foo: true }, { disallowTrailingComma: true });
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' });
assertInvalidParse('{ ,"bar": 8 }', { bar: 8 });
assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 });
@ -213,10 +213,10 @@ suite('JSON', () => {
test('parse: array with errors', () => {
assertInvalidParse('[,]', []);
assertInvalidParse('[ 1, 2, ]', [1, 2]);
assertInvalidParse('[ 1, 2, ]', [1, 2], { disallowTrailingComma: true });
assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]);
assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]);
assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3]);
assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { disallowTrailingComma: true });
});
test('parse: disallow commments', () => {
@ -229,15 +229,19 @@ suite('JSON', () => {
});
test('parse: trailing comma', () => {
let options = { allowTrailingComma: true };
// default is allow
assertValidParse('{ "hello": [], }', { hello: [] });
let options = { disallowTrailingComma: false };
assertValidParse('{ "hello": [], }', { hello: [] }, options);
assertValidParse('{ "hello": [] }', { hello: [] }, options);
assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
assertValidParse('{ "hello": [1,] }', { hello: [1] }, options);
assertInvalidParse('{ "hello": [], }', { hello: [] });
assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} });
options = { disallowTrailingComma: true };
assertInvalidParse('{ "hello": [], }', { hello: [] }, options);
assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
});
test('tree: literals', () => {
@ -320,6 +324,6 @@ suite('JSON', () => {
}
]
}
, [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected]);
, [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { disallowTrailingComma: true });
});
});

View file

@ -42,6 +42,7 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza
import { LocalizationsChannel } from 'vs/platform/localizations/common/localizationsIpc';
import { DialogChannelClient } from 'vs/platform/dialogs/common/dialogIpc';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@ -61,11 +62,13 @@ const eventPrefix = 'monacoworkbench';
function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void {
const services = new ServiceCollection();
const disposables: IDisposable[] = [];
process.once('exit', () => dispose(disposables));
const environmentService = new EnvironmentService(initData.args, process.execPath);
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { route: () => 'main' }));
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
process.once('exit', () => logService.dispose());
disposables.push(logService);
logService.info('main', JSON.stringify(configuration));
@ -98,7 +101,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
// It is important to dispose the AI adapter properly because
// only then they flush remaining data.
process.once('exit', () => appenders.forEach(a => a.dispose()));
disposables.push(...appenders);
const appender = combinedAppender(...appenders);
server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appender));
@ -138,6 +141,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
server.registerChannel('localizations', localizationsChannel);
createSharedProcessContributions(instantiationService2);
disposables.push(extensionManagementService as ExtensionManagementService);
});
});
}

View file

@ -5,7 +5,7 @@
'use strict';
import { app, ipcMain as ipc } from 'electron';
import { app, ipcMain as ipc, systemPreferences } from 'electron';
import * as platform from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
@ -85,7 +85,7 @@ export class CodeApplication {
@ILogService private logService: ILogService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IConfigurationService configurationService: ConfigurationService,
@IConfigurationService private configurationService: ConfigurationService,
@IStateService private stateService: IStateService,
@IHistoryMainService private historyMainService: IHistoryMainService
) {
@ -274,6 +274,20 @@ export class CodeApplication {
app.setAppUserModelId(product.win32AppUserModelId);
}
// Fix native tabs on macOS 10.13
// macOS enables a compatibility patch for any bundle ID beginning with
// "com.microsoft.", which breaks native tabs for VS Code when using this
// identifier (from the official build).
// Explicitly opt out of the patch here before creating any windows.
// See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085
try {
if (platform.isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any);
}
} catch (error) {
this.logService.error(error);
}
// Create Electron IPC Server
this.electronIpcServer = new ElectronIPCServer();

View file

@ -240,9 +240,9 @@ export class CodeMenu {
this.setGotoMenu(gotoMenu);
// Terminal
const terminalMenu = new Menu();
const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu });
this.setTerminalMenu(terminalMenu);
// const terminalMenu = new Menu();
// const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu });
// this.setTerminalMenu(terminalMenu);
// Debug
const debugMenu = new Menu();
@ -277,7 +277,7 @@ export class CodeMenu {
menubar.append(selectionMenuItem);
menubar.append(viewMenuItem);
menubar.append(gotoMenuItem);
menubar.append(terminalMenuItem);
// menubar.append(terminalMenuItem);
menubar.append(debugMenuItem);
menubar.append(taskMenuItem);
@ -864,71 +864,71 @@ export class CodeMenu {
].forEach(item => gotoMenu.append(item));
}
private setTerminalMenu(terminalMenu: Electron.Menu): void {
const newTerminal = this.createMenuItem(nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal"), 'workbench.action.terminal.new');
const splitTerminal = this.createMenuItem(nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), 'workbench.action.terminal.split');
const killTerminal = this.createMenuItem(nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal"), 'workbench.action.terminal.kill');
// private setTerminalMenu(terminalMenu: Electron.Menu): void {
// const newTerminal = this.createMenuItem(nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal"), 'workbench.action.terminal.new');
// const splitTerminal = this.createMenuItem(nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), 'workbench.action.terminal.split');
// const killTerminal = this.createMenuItem(nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal"), 'workbench.action.terminal.kill');
const clear = this.createMenuItem(nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear"), 'workbench.action.terminal.clear');
// const deleteWordLeft = this.createMenuItem(nls.localize({ key: 'miDeleteWordLeft', comment: ['&& denotes a mnemonic'] }, "Delete Word To &&Left"), 'workbench.action.terminal.deleteWordLeft');
// const deleteWordRight = this.createMenuItem(nls.localize({ key: 'miDeleteWordRight', comment: ['&& denotes a mnemonic'] }, "Delete Word To &&Right"), 'workbench.action.terminal.deleteWordRight');
// const moveToLineStart = this.createMenuItem(nls.localize({ key: 'miMoveToLineStart', comment: ['&& denotes a mnemonic'] }, "Move to Line Start"), 'workbench.action.terminal.moveToLineStart');
// const moveToLineEnd = this.createMenuItem(nls.localize({ key: 'miMoveToLineEnd', comment: ['&& denotes a mnemonic'] }, "Move to Line &&End"), 'workbench.action.terminal.moveToLineEnd');
// const clear = this.createMenuItem(nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear"), 'workbench.action.terminal.clear');
// // const deleteWordLeft = this.createMenuItem(nls.localize({ key: 'miDeleteWordLeft', comment: ['&& denotes a mnemonic'] }, "Delete Word To &&Left"), 'workbench.action.terminal.deleteWordLeft');
// // const deleteWordRight = this.createMenuItem(nls.localize({ key: 'miDeleteWordRight', comment: ['&& denotes a mnemonic'] }, "Delete Word To &&Right"), 'workbench.action.terminal.deleteWordRight');
// // const moveToLineStart = this.createMenuItem(nls.localize({ key: 'miMoveToLineStart', comment: ['&& denotes a mnemonic'] }, "Move to Line Start"), 'workbench.action.terminal.moveToLineStart');
// // const moveToLineEnd = this.createMenuItem(nls.localize({ key: 'miMoveToLineEnd', comment: ['&& denotes a mnemonic'] }, "Move to Line &&End"), 'workbench.action.terminal.moveToLineEnd');
const runActiveFile = this.createMenuItem(nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File"), 'workbench.action.terminal.runActiveFile');
const runSelectedText = this.createMenuItem(nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text"), 'workbench.action.terminal.runSelectedText');
// const runActiveFile = this.createMenuItem(nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File"), 'workbench.action.terminal.runActiveFile');
// const runSelectedText = this.createMenuItem(nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text"), 'workbench.action.terminal.runSelectedText');
// const scrollUp = this.createMenuItem(nls.localize({ key: 'miScrollUp', comment: ['&& denotes a mnemonic'] }, "Scroll Up"), 'workbench.action.terminal.scrollUp');
// const scrollDown = this.createMenuItem(nls.localize({ key: 'miScrollDown', comment: ['&& denotes a mnemonic'] }, "Scroll Down"), 'workbench.action.terminal.scrollDown');
// const scrollUpPage = this.createMenuItem(nls.localize({ key: 'miScrollUpPage', comment: ['&& denotes a mnemonic'] }, "Scroll Up Page"), 'workbench.action.terminal.scrollUpPage');
// const scrollDownPage = this.createMenuItem(nls.localize({ key: 'miScrollDownPage', comment: ['&& denotes a mnemonic'] }, "Scroll Down Page"), 'workbench.action.terminal.scrollDownPage');
// const scrollToTop = this.createMenuItem(nls.localize({ key: 'miScrollToTop', comment: ['&& denotes a mnemonic'] }, "Scroll To Top"), 'workbench.action.terminal.scrollToTop');
// const scrollToBottom = this.createMenuItem(nls.localize({ key: 'miScrollToBottom', comment: ['&& denotes a mnemonic'] }, "Scroll To Bottom"), 'workbench.action.terminal.scrollToBottom');
const scrollToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command"), 'workbench.action.terminal.scrollToPreviousCommand');
const scrollToNextCommand = this.createMenuItem(nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command"), 'workbench.action.terminal.scrollToNextCommand');
// // const scrollUp = this.createMenuItem(nls.localize({ key: 'miScrollUp', comment: ['&& denotes a mnemonic'] }, "Scroll Up"), 'workbench.action.terminal.scrollUp');
// // const scrollDown = this.createMenuItem(nls.localize({ key: 'miScrollDown', comment: ['&& denotes a mnemonic'] }, "Scroll Down"), 'workbench.action.terminal.scrollDown');
// // const scrollUpPage = this.createMenuItem(nls.localize({ key: 'miScrollUpPage', comment: ['&& denotes a mnemonic'] }, "Scroll Up Page"), 'workbench.action.terminal.scrollUpPage');
// // const scrollDownPage = this.createMenuItem(nls.localize({ key: 'miScrollDownPage', comment: ['&& denotes a mnemonic'] }, "Scroll Down Page"), 'workbench.action.terminal.scrollDownPage');
// // const scrollToTop = this.createMenuItem(nls.localize({ key: 'miScrollToTop', comment: ['&& denotes a mnemonic'] }, "Scroll To Top"), 'workbench.action.terminal.scrollToTop');
// // const scrollToBottom = this.createMenuItem(nls.localize({ key: 'miScrollToBottom', comment: ['&& denotes a mnemonic'] }, "Scroll To Bottom"), 'workbench.action.terminal.scrollToBottom');
// const scrollToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command"), 'workbench.action.terminal.scrollToPreviousCommand');
// const scrollToNextCommand = this.createMenuItem(nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command"), 'workbench.action.terminal.scrollToNextCommand');
const selectToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command"), 'workbench.action.terminal.selectToPreviousCommand');
const selectToNextCommand = this.createMenuItem(nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command"), 'workbench.action.terminal.selectToNextCommand');
// const selectToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command"), 'workbench.action.terminal.selectToPreviousCommand');
// const selectToNextCommand = this.createMenuItem(nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command"), 'workbench.action.terminal.selectToNextCommand');
const menuItems: MenuItem[] = [
newTerminal,
splitTerminal,
killTerminal,
__separator__(),
clear,
];
// if (!isWindows) {
// menuItems.push(
// deleteWordLeft,
// deleteWordRight,
// );
// const menuItems: MenuItem[] = [
// newTerminal,
// splitTerminal,
// killTerminal,
// __separator__(),
// clear,
// ];
// // if (!isWindows) {
// // menuItems.push(
// // deleteWordLeft,
// // deleteWordRight,
// // );
// }
// if (isMacintosh) {
// menuItems.push(
// moveToLineStart,
// moveToLineEnd
// );
// }
menuItems.push(
runActiveFile,
runSelectedText,
__separator__(),
// scrollUp,
// scrollDown,
// scrollUpPage,
// scrollDownPage,
// scrollToTop,
// scrollToBottom,
scrollToPreviousCommand,
scrollToNextCommand,
// __separator__(),
selectToPreviousCommand,
selectToNextCommand
);
// // }
// // if (isMacintosh) {
// // menuItems.push(
// // moveToLineStart,
// // moveToLineEnd
// // );
// // }
// menuItems.push(
// runActiveFile,
// runSelectedText,
// __separator__(),
// // scrollUp,
// // scrollDown,
// // scrollUpPage,
// // scrollDownPage,
// // scrollToTop,
// // scrollToBottom,
// scrollToPreviousCommand,
// scrollToNextCommand,
// // __separator__(),
// selectToPreviousCommand,
// selectToNextCommand
// );
menuItems.forEach(item => terminalMenu.append(item));
}
// menuItems.forEach(item => terminalMenu.append(item));
// }
private setDebugMenu(debugMenu: Electron.Menu): void {
const start = this.createMenuItem(nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging"), 'workbench.action.debug.start');

View file

@ -38,6 +38,7 @@ import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService';
import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
@ -175,7 +176,7 @@ class Main {
return sequence(extensions.map(extension => () => {
return getExtensionId(extension).then(id => {
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => {
const [extension] = installed.filter(e => getId(e.manifest) === id);
const [extension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id }));
if (!extension) {
return TPromise.wrapError(new Error(`${notInstalled(id)}\n${useId}`));

View file

@ -82,6 +82,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
this._rawOptions.scrollbar = objects.mixin({}, this._rawOptions.scrollbar || {});
this._rawOptions.minimap = objects.mixin({}, this._rawOptions.minimap || {});
this._rawOptions.find = objects.mixin({}, this._rawOptions.find || {});
this._rawOptions.hover = objects.mixin({}, this._rawOptions.hover || {});
this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS);
this.editor = null;
@ -352,6 +353,11 @@ const editorConfiguration: IConfigurationNode = {
'default': EDITOR_DEFAULTS.contribInfo.hover.delay,
'description': nls.localize('hover.delay', "Controls the delay after which to show the hover")
},
'editor.hover.sticky': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.hover.sticky,
'description': nls.localize('hover.sticky', "Controls if the hover should remain visible when mouse is moved over it")
},
'editor.find.seedSearchStringFromSelection': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection,

View file

@ -151,6 +151,11 @@ export interface IEditorHoverOptions {
* Defaults to 300.
*/
delay?: number;
/**
* Is the hover sticky such that it can be clicked and its contents selected?
* Defaults to true.
*/
sticky?: boolean;
}
/**
@ -822,6 +827,7 @@ export interface InternalEditorFindOptions {
export interface InternalEditorHoverOptions {
readonly enabled: boolean;
readonly delay: number;
readonly sticky: boolean;
}
export interface EditorWrappingInfo {
@ -1216,6 +1222,7 @@ export class InternalEditorOptions {
return (
a.enabled === b.enabled
&& a.delay === b.delay
&& a.sticky === b.sticky
);
}
@ -1702,7 +1709,8 @@ export class EditorOptionsValidator {
return {
enabled: _boolean(opts.enabled, defaults.enabled),
delay: _clampedInt(opts.delay, defaults.delay, 0, 10000)
delay: _clampedInt(opts.delay, defaults.delay, 0, 10000),
sticky: _boolean(opts.sticky, defaults.sticky)
};
}
@ -2401,7 +2409,8 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
selectionClipboard: true,
hover: {
enabled: true,
delay: 300
delay: 300,
sticky: true
},
links: true,
contextmenu: true,

View file

@ -906,7 +906,7 @@ export function isResourceTextEdit(thing: any): thing is ResourceTextEdit {
export interface ResourceFileEdit {
oldUri: URI;
newUri: URI;
options: { overwrite?: boolean, ignoreIfExists?: boolean };
options: { overwrite?: boolean, ignoreIfExists?: boolean, recursive?: boolean };
}
export interface ResourceTextEdit {

View file

@ -197,10 +197,33 @@ export class OutlineGroup extends TreeElement {
export class OutlineModel extends TreeElement {
private static readonly _requests = new LRUCache<string, { promiseCnt: number, promise: TPromise<any>, model: OutlineModel }>(9, .75);
private static readonly _keys = new class {
private _counter = 1;
private _data = new WeakMap<DocumentSymbolProvider, number>();
for(textModel: ITextModel): string {
return `${textModel.id}/${textModel.getVersionId()}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`;
}
private _hash(providers: DocumentSymbolProvider[]): string {
let result = '';
for (const provider of providers) {
let n = this._data.get(provider);
if (typeof n === 'undefined') {
n = this._counter++;
this._data.set(provider, n);
}
result += n;
}
return result;
}
};
static create(textModel: ITextModel): TPromise<OutlineModel> {
let key = `${textModel.id}/${textModel.getVersionId()}/${DocumentSymbolProviderRegistry.all(textModel).length}`;
let key = this._keys.for(textModel);
let data = OutlineModel._requests.get(key);
if (!data) {

View file

@ -181,11 +181,12 @@ export class OutlineRenderer implements IRenderer {
}
const { count, topSev } = element.marker;
const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground).toString();
const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
const cssColor = color ? color.toString() : 'inherit';
// color of the label
if (this.renderProblemColors) {
template.labelContainer.style.setProperty('--outline-element-color', color);
template.labelContainer.style.setProperty('--outline-element-color', cssColor);
} else {
template.labelContainer.style.removeProperty('--outline-element-color');
}
@ -199,14 +200,14 @@ export class OutlineRenderer implements IRenderer {
dom.removeClass(template.decoration, 'bubble');
template.decoration.innerText = count < 10 ? count.toString() : '+9';
template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count);
template.decoration.style.setProperty('--outline-element-color', color);
template.decoration.style.setProperty('--outline-element-color', cssColor);
} else {
dom.show(template.decoration);
dom.addClass(template.decoration, 'bubble');
template.decoration.innerText = '\uf052';
template.decoration.title = localize('deep.problem', "Contains elements with problems");
template.decoration.style.setProperty('--outline-element-color', color);
template.decoration.style.setProperty('--outline-element-color', cssColor);
}
}

View file

@ -312,10 +312,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _updateHistory() {
if (this._state.searchString) {
this._findInput.inputBox.addToHistory(this._state.searchString);
this._findInput.inputBox.addToHistory();
}
if (this._state.replaceString) {
this._replaceInputBox.addToHistory(this._state.replaceString);
this._replaceInputBox.addToHistory();
}
}

View file

@ -133,6 +133,10 @@ export abstract class SimpleFindWidget extends Widget {
return this._findInput.getValue();
}
public get focusTracker(): dom.IFocusTracker {
return this._findInputFocusTracker;
}
public updateTheme(theme: ITheme): void {
const inputStyles = {
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
@ -197,9 +201,7 @@ export abstract class SimpleFindWidget extends Widget {
}
protected _updateHistory() {
if (this.inputValue) {
this._findInput.inputBox.addToHistory(this._findInput.getValue());
}
this._findInput.inputBox.addToHistory();
}
}

View file

@ -13,7 +13,8 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IEditorContribution, IScrollEvent } from 'vs/editor/common/editorCommon';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { ModesContentHoverWidget } from './modesContentHover';
@ -26,12 +27,12 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation';
export class ModesHoverController implements editorCommon.IEditorContribution {
export class ModesHoverController implements IEditorContribution {
private static readonly ID = 'editor.contrib.hover';
private _editor: ICodeEditor;
private _toUnhook: IDisposable[];
private _didChangeConfigurationHandler: IDisposable;
private _contentWidget: ModesContentHoverWidget;
private _glyphWidget: ModesGlyphHoverWidget;
@ -52,35 +53,57 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
private _isMouseDown: boolean;
private _hoverClicked: boolean;
private _isHoverEnabled: boolean;
private _isHoverSticky: boolean;
static get(editor: ICodeEditor): ModesHoverController {
return editor.getContribution<ModesHoverController>(ModesHoverController.ID);
}
constructor(editor: ICodeEditor,
constructor(private readonly _editor: ICodeEditor,
@IOpenerService private readonly _openerService: IOpenerService,
@IModeService private readonly _modeService: IModeService,
@IThemeService private readonly _themeService: IThemeService
) {
this._editor = editor;
this._toUnhook = [];
this._isMouseDown = false;
if (editor.getConfiguration().contribInfo.hover) {
this._isMouseDown = false;
this._hoverClicked = false;
this._hookEvents();
this._didChangeConfigurationHandler = this._editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
if (e.contribInfo) {
this._hideWidgets();
this._unhookEvents();
this._hookEvents();
}
});
}
private _hookEvents(): void {
const hideWidgetsEventHandler = () => this._hideWidgets();
const hoverOpts = this._editor.getConfiguration().contribInfo.hover;
this._isHoverEnabled = hoverOpts.enabled;
this._isHoverSticky = hoverOpts.sticky;
if (this._isHoverEnabled) {
this._toUnhook.push(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e)));
this._toUnhook.push(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e)));
this._toUnhook.push(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e)));
this._toUnhook.push(this._editor.onMouseLeave((e: IEditorMouseEvent) => this._hideWidgets()));
this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e)));
this._toUnhook.push(this._editor.onDidChangeModel(() => this._hideWidgets()));
this._toUnhook.push(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged()));
this._toUnhook.push(this._editor.onDidScrollChange((e) => {
if (e.scrollTopChanged || e.scrollLeftChanged) {
this._hideWidgets();
}
}));
} else {
this._toUnhook.push(this._editor.onMouseMove(hideWidgetsEventHandler));
}
this._toUnhook.push(this._editor.onMouseLeave(hideWidgetsEventHandler));
this._toUnhook.push(this._editor.onDidChangeModel(hideWidgetsEventHandler));
this._toUnhook.push(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e)));
}
private _unhookEvents(): void {
this._toUnhook = dispose(this._toUnhook);
}
private _onModelDecorationsChanged(): void {
@ -88,6 +111,12 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
this.glyphWidget.onModelDecorationsChanged();
}
private _onEditorScrollChanged(e: IScrollEvent): void {
if (e.scrollTopChanged || e.scrollLeftChanged) {
this._hideWidgets();
}
}
private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void {
this._isMouseDown = true;
@ -116,6 +145,7 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void {
// const this._editor.getConfiguration().contribInfo.hover.sticky;
let targetType = mouseEvent.target.type;
const hasStopKey = (platform.isMacintosh ? mouseEvent.event.metaKey : mouseEvent.event.ctrlKey);
@ -123,12 +153,12 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
return;
}
if (targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID && !hasStopKey) {
if (this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID && !hasStopKey) {
// mouse moved on top of content hover widget
return;
}
if (targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID && !hasStopKey) {
if (this._isHoverSticky && targetType === MouseTargetType.OVERLAY_WIDGET && mouseEvent.target.detail === ModesGlyphHoverWidget.ID && !hasStopKey) {
// mouse moved on top of overlay hover widget
return;
}
@ -142,12 +172,18 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
}
if (this._editor.getConfiguration().contribInfo.hover && targetType === MouseTargetType.CONTENT_TEXT) {
if (targetType === MouseTargetType.CONTENT_TEXT) {
this.glyphWidget.hide();
this.contentWidget.startShowingAt(mouseEvent.target.range, HoverStartMode.Delayed, false);
if (this._isHoverEnabled) {
this.contentWidget.startShowingAt(mouseEvent.target.range, HoverStartMode.Delayed, false);
}
} else if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN) {
this.contentWidget.hide();
this.glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
if (this._isHoverEnabled) {
this.glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
}
} else {
this._hideWidgets();
}
@ -184,7 +220,9 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
public dispose(): void {
this._toUnhook = dispose(this._toUnhook);
this._unhookEvents();
this._didChangeConfigurationHandler.dispose();
if (this._glyphWidget) {
this._glyphWidget.dispose();
this._glyphWidget = null;

View file

@ -73,10 +73,12 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
}
public dispose(): void {
if (this._widget) {
this._widget.dispose();
this._widget = null;
}
this._referenceSearchVisible.reset();
dispose(this._disposables);
dispose(this._widget);
dispose(this._model);
this._widget = null;
this._model = null;
this._editor = null;
}
@ -189,16 +191,12 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
}
public closeWidget(): void {
if (this._widget) {
this._widget.dispose();
this._widget = null;
}
dispose(this._widget);
this._widget = null;
this._referenceSearchVisible.reset();
this._disposables = dispose(this._disposables);
if (this._model) {
this._model.dispose();
this._model = null;
}
dispose(this._model);
this._model = null;
this._editor.focus();
this._requestIdPool += 1; // Cancel pending requests
}

View file

@ -85,9 +85,7 @@ tabstop ::= '$' int
| '${' int '}'
| '${' int transform '}'
placeholder ::= '${' int ':' any '}'
| '${' int ':' any transform '}'
choice ::= '${' int '|' text (',' text)* '|}'
| '${' int '|' text (',' text)* '|' transform '}'
variable ::= '$' var | '${' var }'
| '${' var ':' any '}'
| '${' var transform '}'
@ -103,5 +101,3 @@ var ::= [_a-zA-Z] [_a-zA-Z0-9]*
int ::= [0-9]+
text ::= .*
```
Transformations for placeholders and choices are an extension to the TextMate snippet grammar and only support by Visual Studio Code.

View file

@ -334,7 +334,7 @@ export class Transform extends Marker {
}
toTextmateString(): string {
return `/${Text.escape(this.regexp.source)}/${this.children.map(c => c.toTextmateString())}/${this.regexp.ignoreCase ? 'i' : ''}`;
return `/${Text.escape(this.regexp.source)}/${this.children.map(c => c.toTextmateString())}/${(this.regexp.ignoreCase ? 'i' : '') + (this.regexp.global ? 'g' : '')}`;
}
clone(): Transform {
@ -643,6 +643,9 @@ export class SnippetParser {
let start = this._token;
while (this._token.type !== type) {
this._token = this._scanner.next();
if (this._token.type === TokenType.EOF) {
return false;
}
}
let value = this._scanner.value.substring(start.pos, this._token.pos);
this._token = this._scanner.next();
@ -715,17 +718,6 @@ export class SnippetParser {
return true;
}
//../<regex>/<format>/<options>} -> transform
if (this._accept(TokenType.Forwardslash)) {
if (this._parseTransform(placeholder)) {
parent.appendChild(placeholder);
return true;
}
this._backTo(token);
return false;
}
if (this._parse(placeholder)) {
continue;
}
@ -754,13 +746,6 @@ export class SnippetParser {
parent.appendChild(placeholder);
return true;
}
if (this._accept(TokenType.Forwardslash)) {
// ...|/<regex>/<format>/<options>} -> transform
if (this._parseTransform(placeholder)) {
parent.appendChild(placeholder);
return true;
}
}
}
}

View file

@ -9,7 +9,7 @@ import 'vs/css!./snippetSession';
import { getLeadingWhitespace } from 'vs/base/common/strings';
import { ITextModel, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TextmateSnippet, Placeholder, Choice, SnippetParser } from './snippetParser';
import { TextmateSnippet, Placeholder, Choice, Text, SnippetParser } from './snippetParser';
import { Selection } from 'vs/editor/common/core/selection';
import { Range } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
@ -98,10 +98,12 @@ export class OneSnippet {
const range = this._editor.getModel().getDecorationRange(id);
const currentValue = this._editor.getModel().getValueInRange(range);
operations.push({ range: range, text: placeholder.transform.resolve(currentValue) });
operations.push(EditOperation.replaceMove(range, placeholder.transform.resolve(currentValue)));
}
}
this._editor.getModel().applyEdits(operations);
if (operations.length > 0) {
this._editor.executeEdits('snippet.placeholderTransform', operations);
}
}
if (fwd === true && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) {
@ -182,6 +184,14 @@ export class OneSnippet {
const id = this._placeholderDecorations.get(placeholder);
const range = this._editor.getModel().getDecorationRange(id);
if (!range) {
// one of the placeholder lost its decoration and
// therefore we bail out and pretend the placeholder
// (with its mirrors) doesn't exist anymore.
result.delete(placeholder.index);
break;
}
ranges.push(range);
}
}
@ -261,17 +271,26 @@ export class OneSnippet {
export class SnippetSession {
static adjustWhitespace(model: ITextModel, position: IPosition, template: string): string {
static adjustWhitespace2(model: ITextModel, position: IPosition, snippet: TextmateSnippet): void {
const line = model.getLineContent(position.lineNumber);
const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1);
const templateLines = template.split(/\r\n|\r|\n/);
for (let i = 1; i < templateLines.length; i++) {
let templateLeadingWhitespace = getLeadingWhitespace(templateLines[i]);
templateLines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + templateLines[i].substr(templateLeadingWhitespace.length);
}
return templateLines.join(model.getEOL());
snippet.walk(marker => {
if (marker instanceof Text && !(marker.parent instanceof Choice)) {
// adjust indentation of text markers, except for choise elements
// which get adjusted when being selected
const lines = marker.value.split(/\r\n|\r|\n/);
for (let i = 1; i < lines.length; i++) {
let templateLeadingWhitespace = getLeadingWhitespace(lines[i]);
lines[i] = model.normalizeIndentation(lineLeadingWhitespace + templateLeadingWhitespace) + lines[i].substr(templateLeadingWhitespace.length);
}
const newValue = lines.join(model.getEOL());
if (newValue !== marker.value) {
marker.parent.replace(marker, [new Text(newValue)]);
}
}
return true;
});
}
static adjustSelection(model: ITextModel, selection: Selection, overwriteBefore: number, overwriteAfter: number): Selection {
@ -341,19 +360,19 @@ export class SnippetSession {
.setStartPosition(extensionBefore.startLineNumber, extensionBefore.startColumn)
.setEndPosition(extensionAfter.endLineNumber, extensionAfter.endColumn);
const snippet = new SnippetParser().parse(template, true, enforceFinalTabstop);
// adjust the template string to match the indentation and
// whitespace rules of this insert location (can be different for each cursor)
const start = snippetSelection.getStartPosition();
const adjustedTemplate = SnippetSession.adjustWhitespace(model, start, template);
SnippetSession.adjustWhitespace2(model, start, snippet);
const snippet = new SnippetParser()
.parse(adjustedTemplate, true, enforceFinalTabstop)
.resolveVariables(new CompositeSnippetVariableResolver([
modelBasedVariableResolver,
new ClipboardBasedVariableResolver(clipboardService, idx, indexedSelections.length),
new SelectionBasedVariableResolver(model, selection),
new TimeBasedVariableResolver
]));
snippet.resolveVariables(new CompositeSnippetVariableResolver([
modelBasedVariableResolver,
new ClipboardBasedVariableResolver(clipboardService, idx, indexedSelections.length),
new SelectionBasedVariableResolver(model, selection),
new TimeBasedVariableResolver
]));
const offset = model.getOffsetAt(start) + delta;
delta += snippet.toString().length - model.getValueLengthInRange(snippetSelection);

View file

@ -254,21 +254,6 @@ suite('SnippetParser', () => {
assertTextAndMarker('${1/regex/format/options', '${1/regex/format/options', Text);
});
test('Parser, placeholder with defaults and transformation', () => {
assertTextAndMarker('${1:value/foo/bar/}', 'value', Placeholder);
assertTextAndMarker('${1:bar${2:foo}bar/foo/bar/}', 'barfoobar', Placeholder);
// incomplete
assertTextAndMarker('${1:bar${2:foobar}/foo/bar/', '${1:barfoobar/foo/bar/', Text, Placeholder, Text);
});
test('Parser, placeholder with choice and transformation', () => {
assertTextAndMarker('${1|one,two,three|/foo/bar/}', 'one', Placeholder);
assertTextAndMarker('${1|one|/foo/bar/}', 'one', Placeholder);
assertTextAndMarker('${1|one,two,three,|/foo/bar/}', '${1|one,two,three,|/foo/bar/}', Text);
assertTextAndMarker('${1|one,/foo/bar/', '${1|one,/foo/bar/', Text);
});
test('No way to escape forward slash in snippet regex #36715', function () {
assertMarker('${TM_DIRECTORY/src\\//$1/}', Variable);
});
@ -408,32 +393,37 @@ suite('SnippetParser', () => {
});
test('Parser, transform example', () => {
let marker = new SnippetParser().parse('${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
let childs = marker.children;
let { children } = new SnippetParser().parse('${1:name} : ${2:type}${3/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
assert.ok(childs[0] instanceof Placeholder);
assert.equal(childs[0].children.length, 1);
assert.equal(childs[0].children[0].toString(), 'name');
assert.equal((<Placeholder>childs[0]).transform, undefined);
assert.ok(childs[1] instanceof Text);
assert.equal(childs[1].toString(), ' : ');
assert.ok(childs[2] instanceof Placeholder);
assert.equal(childs[2].children.length, 1);
assert.equal(childs[2].children[0].toString(), 'type');
assert.ok(childs[3] instanceof Placeholder);
assert.equal(childs[3].children.length, 1);
assert.equal(childs[3].children[0].toString(), ' :=');
assert.notEqual((<Placeholder>childs[3]).transform, undefined);
let t = (<Placeholder>childs[3]).transform;
assert.equal(t.regexp, '/\\s:=(.*)/');
assert.equal(t.children.length, 2);
assert.ok(t.children[0] instanceof FormatString);
assert.equal((<FormatString>t.children[0]).index, 1);
assert.equal((<FormatString>t.children[0]).ifValue, ' :=');
assert.ok(t.children[1] instanceof FormatString);
assert.equal((<FormatString>t.children[1]).index, 1);
assert.ok(childs[4] instanceof Text);
assert.equal(childs[4].toString(), ';\n');
//${1:name}
assert.ok(children[0] instanceof Placeholder);
assert.equal(children[0].children.length, 1);
assert.equal(children[0].children[0].toString(), 'name');
assert.equal((<Placeholder>children[0]).transform, undefined);
// :
assert.ok(children[1] instanceof Text);
assert.equal(children[1].toString(), ' : ');
//${2:type}
assert.ok(children[2] instanceof Placeholder);
assert.equal(children[2].children.length, 1);
assert.equal(children[2].children[0].toString(), 'type');
//${3/\\s:=(.*)/${1:+ :=}${1}/}
assert.ok(children[3] instanceof Placeholder);
assert.equal(children[3].children.length, 0);
assert.notEqual((<Placeholder>children[3]).transform, undefined);
let transform = (<Placeholder>children[3]).transform;
assert.equal(transform.regexp, '/\\s:=(.*)/');
assert.equal(transform.children.length, 2);
assert.ok(transform.children[0] instanceof FormatString);
assert.equal((<FormatString>transform.children[0]).index, 1);
assert.equal((<FormatString>transform.children[0]).ifValue, ' :=');
assert.ok(transform.children[1] instanceof FormatString);
assert.equal((<FormatString>transform.children[1]).index, 1);
assert.ok(children[4] instanceof Text);
assert.equal(children[4].toString(), ';\n');
});
@ -454,20 +444,6 @@ suite('SnippetParser', () => {
test('Parser, default placeholder values and one transform', () => {
assertMarker('errorContext: `${1:err/err/ok/}`, error: $1', Text, Placeholder, Text, Placeholder);
const [, p1, , p2] = new SnippetParser().parse('errorContext: `${1:err/err/ok/}`, error:$1').children;
assert.equal((<Placeholder>p1).index, '1');
assert.equal((<Placeholder>p1).children.length, '1');
assert.equal((<Text>(<Placeholder>p1).children[0]), 'err');
assert.notEqual((<Placeholder>p1).transform, undefined);
assert.equal((<Placeholder>p2).index, '1');
assert.equal((<Placeholder>p2).children.length, '1');
assert.equal((<Text>(<Placeholder>p2).children[0]), 'err');
assert.equal((<Placeholder>p2).transform, undefined);
assertMarker('errorContext: `${1:err}`, error: ${1/err/ok/}', Text, Placeholder, Text, Placeholder);
const [, p3, , p4] = new SnippetParser().parse('errorContext: `${1:err}`, error:${1/err/ok/}').children;
@ -720,4 +696,20 @@ suite('SnippetParser', () => {
const snippet = new SnippetParser().parse('${TM_DIRECTORY/.*src[\\/](.*)/$1/}');
assertMarker(snippet, Variable);
});
test('Variable transformation doesn\'t work if undefined variables are used in the same snippet #51769', function () {
let transform = new Transform();
transform.appendChild(new Text('bar'));
transform.regexp = new RegExp('foo', 'gi');
assert.equal(transform.toTextmateString(), '/foo/bar/ig');
});
test('Snippet parser freeze #53144', function () {
let snippet = new SnippetParser().parse('${1/(void$)|(.+)/${1:?-\treturn nil;}/}');
assertMarker(snippet, Placeholder);
});
test('snippets variable not resolved in JSON proposal #52931', function () {
assertTextAndMarker('FOO${1:/bin/bash}', 'FOO/bin/bash', Text, Placeholder);
});
});

View file

@ -12,6 +12,7 @@ import { SnippetSession } from 'vs/editor/contrib/snippet/snippetSession';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { TextModel } from 'vs/editor/common/model/textModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
suite('SnippetSession', function () {
@ -41,8 +42,9 @@ suite('SnippetSession', function () {
test('normalize whitespace', function () {
function assertNormalized(position: IPosition, input: string, expected: string): void {
const actual = SnippetSession.adjustWhitespace(model, position, input);
assert.equal(actual, expected);
const snippet = new SnippetParser().parse(input);
SnippetSession.adjustWhitespace2(model, position, snippet);
assert.equal(snippet.toTextmateString(), expected);
}
assertNormalized(new Position(1, 1), 'foo', 'foo');
@ -51,6 +53,9 @@ suite('SnippetSession', function () {
assertNormalized(new Position(2, 5), 'foo\r\tbar', 'foo\n bar');
assertNormalized(new Position(2, 3), 'foo\r\tbar', 'foo\n bar');
assertNormalized(new Position(2, 5), 'foo\r\tbar\nfoo', 'foo\n bar\n foo');
//Indentation issue with choice elements that span multiple lines #46266
assertNormalized(new Position(2, 5), 'a\nb${1|foo,\nbar|}', 'a\n b${1|foo,\nbar|}');
});
test('adjust selection (overwrite[Before|After])', function () {
@ -467,7 +472,7 @@ suite('SnippetSession', function () {
test('snippets, transform example', function () {
editor.getModel().setValue('');
editor.setSelection(new Selection(1, 1, 1, 1));
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
session.insert();
assertSelections(editor, new Selection(1, 1, 1, 5));
@ -478,7 +483,7 @@ suite('SnippetSession', function () {
editor.trigger('test', 'type', { text: 'std_logic' });
session.next();
assertSelections(editor, new Selection(1, 16, 1, 19));
assertSelections(editor, new Selection(1, 16, 1, 16));
session.next();
assert.equal(model.getValue(), 'clk : std_logic;\n');
@ -529,7 +534,7 @@ suite('SnippetSession', function () {
test('snippets, transform example hit if', function () {
editor.getModel().setValue('');
editor.setSelection(new Selection(1, 1, 1, 1));
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3/\\s:=(.*)/${1:+ :=}${1}/};\n$0');
session.insert();
assertSelections(editor, new Selection(1, 1, 1, 5));
@ -540,7 +545,7 @@ suite('SnippetSession', function () {
editor.trigger('test', 'type', { text: 'std_logic' });
session.next();
assertSelections(editor, new Selection(1, 16, 1, 19));
assertSelections(editor, new Selection(1, 16, 1, 16));
editor.trigger('test', 'type', { text: ' := \'1\'' });
session.next();

View file

@ -37,6 +37,10 @@ export type SnippetConfig = 'top' | 'bottom' | 'inline' | 'none';
let _snippetSuggestSupport: ISuggestSupport;
export function getSnippetSuggestSupport(): ISuggestSupport {
return _snippetSuggestSupport;
}
export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupport {
const old = _snippetSuggestSupport;
_snippetSuggestSupport = support;

View file

@ -355,7 +355,7 @@ registerEditorCommand(new SuggestCommand({
handler: x => x.acceptSelectedSuggestion(),
kbOpts: {
weight: weight,
kbExpr: ContextKeyExpr.and(EditorContextKeys.textInputFocus, SnippetController2.InSnippetMode.toNegated()),
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyCode.Tab
}
}));

View file

@ -18,7 +18,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { ISuggestSupport, StandardTokenType, SuggestContext, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes';
import { CompletionModel } from './completionModel';
import { ISuggestionItem, getSuggestionComparator, provideSuggestionItems } from './suggest';
import { ISuggestionItem, getSuggestionComparator, provideSuggestionItems, getSnippetSuggestSupport } from './suggest';
export interface ICancelEvent {
readonly retrigger: boolean;
@ -180,6 +180,7 @@ export class SuggestModel implements IDisposable {
let set = supportsByTriggerCharacter[ch];
if (!set) {
set = supportsByTriggerCharacter[ch] = new Set();
set.add(getSnippetSuggestSupport());
}
set.add(support);
}

View file

@ -9,6 +9,7 @@ import { EditorZoom } from 'vs/editor/common/config/editorZoom';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { AccessibilitySupport } from 'vs/base/common/platform';
import { IEditorHoverOptions } from 'vs/editor/common/config/editorOptions';
suite('Common Editor Config', () => {
test('Zoom Level', () => {
@ -176,4 +177,17 @@ suite('Common Editor Config', () => {
});
assertWrapping(config, true, 1);
});
test('issue #53152: Cannot assign to read only property \'enabled\' of object', () => {
let hoverOptions: IEditorHoverOptions = {};
Object.defineProperty(hoverOptions, 'enabled', {
writable: false,
value: true
});
let config = new TestConfiguration({ hover: hoverOptions });
assert.equal(config.editor.contribInfo.hover.enabled, true);
config.updateOptions({ hover: { enabled: false } });
assert.equal(config.editor.contribInfo.hover.enabled, false);
});
});

7
src/vs/monaco.d.ts vendored
View file

@ -2502,6 +2502,11 @@ declare namespace monaco.editor {
* Defaults to 300.
*/
delay?: number;
/**
* Is the hover sticky such that it can be clicked and its contents selected?
* Defaults to true.
*/
sticky?: boolean;
}
/**
@ -3102,6 +3107,7 @@ declare namespace monaco.editor {
export interface InternalEditorHoverOptions {
readonly enabled: boolean;
readonly delay: number;
readonly sticky: boolean;
}
export interface EditorWrappingInfo {
@ -5185,6 +5191,7 @@ declare namespace monaco.languages {
options: {
overwrite?: boolean;
ignoreIfExists?: boolean;
recursive?: boolean;
};
}

View file

@ -7,7 +7,7 @@
import { localize } from 'vs/nls';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMenu, MenuItemAction, IMenuActionOptions, ICommandAction } from 'vs/platform/actions/common/actions';
import { IMenu, MenuItemAction, IMenuActionOptions, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IAction } from 'vs/base/common/actions';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@ -91,11 +91,11 @@ export function fillInActionBarActions(menu: IMenu, options: IMenuActionOptions,
fillInActions(groups, target, false, isPrimaryGroup);
}
function fillInActions(groups: [string, MenuItemAction[]][], target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, getAlternativeActions, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
function fillInActions(groups: [string, (MenuItemAction | SubmenuItemAction)[]][], target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, getAlternativeActions, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
for (let tuple of groups) {
let [group, actions] = tuple;
if (getAlternativeActions) {
actions = actions.map(a => !!a.alt ? a.alt : a);
actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a);
}
if (isPrimaryGroup(group)) {

View file

@ -43,6 +43,22 @@ export interface IMenuItem {
order?: number;
}
export interface ISubmenuItem {
title: string | ILocalizedString;
submenu: MenuId;
when?: ContextKeyExpr;
group?: 'navigation' | string;
order?: number;
}
export function isIMenuItem(item: IMenuItem | ISubmenuItem): item is IMenuItem {
return (item as IMenuItem).command !== undefined;
}
export function isISubmenuItem(item: IMenuItem | ISubmenuItem): item is ISubmenuItem {
return (item as ISubmenuItem).submenu !== undefined;
}
export class MenuId {
private static ID = 1;
@ -81,6 +97,7 @@ export class MenuId {
static readonly MenubarWindowMenu = new MenuId();
static readonly MenubarPreferencesMenu = new MenuId();
static readonly MenubarHelpMenu = new MenuId();
static readonly MenubarTerminalMenu = new MenuId();
readonly id: string = String(MenuId.ID++);
}
@ -92,7 +109,7 @@ export interface IMenuActionOptions {
export interface IMenu extends IDisposable {
onDidChange: Event<IMenu>;
getActions(options?: IMenuActionOptions): [string, MenuItemAction[]][];
getActions(options?: IMenuActionOptions): [string, (MenuItemAction | SubmenuItemAction)[]][];
}
export const IMenuService = createDecorator<IMenuService>('menuService');
@ -107,15 +124,15 @@ export interface IMenuService {
export interface IMenuRegistry {
addCommand(userCommand: ICommandAction): boolean;
getCommand(id: string): ICommandAction;
appendMenuItem(menu: MenuId, item: IMenuItem): IDisposable;
getMenuItems(loc: MenuId): IMenuItem[];
appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;
getMenuItems(loc: MenuId): (IMenuItem | ISubmenuItem)[];
}
export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
private _commands: { [id: string]: ICommandAction } = Object.create(null);
private _menuItems: { [loc: string]: IMenuItem[] } = Object.create(null);
private _menuItems: { [loc: string]: (IMenuItem | ISubmenuItem)[] } = Object.create(null);
addCommand(command: ICommandAction): boolean {
const old = this._commands[command.id];
@ -127,7 +144,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
return this._commands[id];
}
appendMenuItem({ id }: MenuId, item: IMenuItem): IDisposable {
appendMenuItem({ id }: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {
let array = this._menuItems[id];
if (!array) {
this._menuItems[id] = array = [item];
@ -144,7 +161,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
};
}
getMenuItems({ id }: MenuId): IMenuItem[] {
getMenuItems({ id }: MenuId): (IMenuItem | ISubmenuItem)[] {
const result = this._menuItems[id] || [];
if (id === MenuId.CommandPalette.id) {
@ -155,9 +172,12 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
return result;
}
private _appendImplicitItems(result: IMenuItem[]) {
private _appendImplicitItems(result: (IMenuItem | ISubmenuItem)[]) {
const set = new Set<string>();
for (const { command, alt } of result) {
const temp = result.filter(item => { return isIMenuItem(item); }) as IMenuItem[];
for (const { command, alt } of temp) {
set.add(command.id);
if (alt) {
set.add(alt.id);
@ -186,6 +206,16 @@ export class ExecuteCommandAction extends Action {
}
}
export class SubmenuItemAction extends Action {
// private _options: IMenuActionOptions;
readonly item: ISubmenuItem;
constructor(item: ISubmenuItem) {
typeof item.title === 'string' ? super('', item.title, 'submenu') : super('', item.title.value, 'submenu');
this.item = item;
}
}
export class MenuItemAction extends ExecuteCommandAction {
private _options: IMenuActionOptions;

View file

@ -9,10 +9,10 @@ import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, MenuRegistry, MenuItemAction, IMenu, IMenuItem, IMenuActionOptions } from 'vs/platform/actions/common/actions';
import { MenuId, MenuRegistry, MenuItemAction, IMenu, IMenuItem, IMenuActionOptions, ISubmenuItem, SubmenuItemAction, isIMenuItem } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
type MenuItemGroup = [string, IMenuItem[]];
type MenuItemGroup = [string, (IMenuItem | ISubmenuItem)[]];
export class Menu implements IMenu {
@ -66,14 +66,14 @@ export class Menu implements IMenu {
return this._onDidChange.event;
}
getActions(options: IMenuActionOptions): [string, MenuItemAction[]][] {
const result: [string, MenuItemAction[]][] = [];
getActions(options: IMenuActionOptions): [string, (MenuItemAction | SubmenuItemAction)[]][] {
const result: [string, (MenuItemAction | SubmenuItemAction)[]][] = [];
for (let group of this._menuGroups) {
const [id, items] = group;
const activeActions: MenuItemAction[] = [];
const activeActions: (MenuItemAction | SubmenuItemAction)[] = [];
for (const item of items) {
if (this._contextKeyService.contextMatchesRules(item.when)) {
const action = new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService);
const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) : new SubmenuItemAction(item);
action.order = item.order; //TODO@Ben order is menu item property, not an action property
activeActions.push(action);
}

View file

@ -84,6 +84,7 @@ export interface IConfigurationNode {
allOf?: IConfigurationNode[];
overridable?: boolean;
scope?: ConfigurationScope;
contributedByExtension?: boolean;
}
export interface IDefaultConfigurationExtension {

View file

@ -105,7 +105,8 @@ export class ContextMenuHandler {
let menu = new Menu(container, actions, {
actionItemProvider: delegate.getActionItem,
context: delegate.getActionsContext ? delegate.getActionsContext() : null,
actionRunner: this.actionRunner
actionRunner: this.actionRunner,
getKeyBinding: delegate.getKeyBinding
});
let listener1 = menu.onDidCancel(() => {

View file

@ -12,6 +12,7 @@ import { IPager } from 'vs/base/common/paging';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILocalization } from 'vs/platform/localizations/common/localizations';
import URI from 'vs/base/common/uri';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$';
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
@ -377,11 +378,6 @@ export interface IExtensionEnablementService {
setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>;
}
export interface IIgnoredRecommendations {
global: string[];
workspace: string[];
}
export interface IExtensionsConfigContent {
recommendations: string[];
unwantedRecommendations: string[];
@ -392,19 +388,30 @@ export type RecommendationChangeNotification = {
isRecommended: boolean
};
export type DynamicRecommendation = 'dynamic';
export type ExecutableRecommendation = 'executable';
export type CachedRecommendation = 'cached';
export type ApplicationRecommendation = 'application';
export type ExtensionRecommendationSource = IWorkspace | IWorkspaceFolder | URI | DynamicRecommendation | ExecutableRecommendation | CachedRecommendation | ApplicationRecommendation;
export interface IExtensionRecommendation {
extensionId: string;
sources: ExtensionRecommendationSource[];
}
export const IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');
export interface IExtensionTipsService {
_serviceBrand: any;
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; };
getFileBasedRecommendations(): string[];
getOtherRecommendations(): TPromise<string[]>;
getWorkspaceRecommendations(): TPromise<string[]>;
getKeymapRecommendations(): string[];
getFileBasedRecommendations(): IExtensionRecommendation[];
getOtherRecommendations(): TPromise<IExtensionRecommendation[]>;
getWorkspaceRecommendations(): TPromise<IExtensionRecommendation[]>;
getKeymapRecommendations(): IExtensionRecommendation[];
getAllRecommendations(): TPromise<IExtensionRecommendation[]>;
getKeywordsForExtension(extension: string): string[];
getRecommendationsForExtension(extension: string): string[];
getAllIgnoredRecommendations(): IIgnoredRecommendations;
ignoreExtensionRecommendation(extensionId: string): void;
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void;
getAllIgnoredRecommendations(): { global: string[], workspace: string[] };
onRecommendationChange: Event<RecommendationChangeNotification>;
}
@ -412,7 +419,8 @@ export enum ExtensionRecommendationReason {
Workspace,
File,
Executable,
DynamicWorkspace
DynamicWorkspace,
Experimental
}
export const ExtensionsLabel = localize('extensions', "Extensions");

View file

@ -303,7 +303,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
publisher: galleryExtension.publisher.publisherName,
publisherDisplayName: galleryExtension.publisher.displayName,
description: galleryExtension.shortDescription || '',
installCount: getStatistic(galleryExtension.statistics, 'install'),
installCount: getStatistic(galleryExtension.statistics, 'install') + getStatistic(galleryExtension.statistics, 'updateCount'),
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
assets,

View file

@ -114,6 +114,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private lastReportTimestamp = 0;
private readonly installationStartTime: Map<string, number> = new Map<string, number>();
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
private readonly uninstallingExtensions: Map<string, TPromise<void>> = new Map<string, TPromise<void>>();
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionLifecycle: ExtensionsLifecycle;
@ -140,9 +141,15 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.extensionsPath = environmentService.extensionsPath;
this.uninstalledPath = path.join(this.extensionsPath, '.obsolete');
this.uninstalledFileLimiter = new Limiter(1);
this._register(toDisposable(() => this.installingExtensions.clear()));
this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this));
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
this._register(toDisposable(() => {
this.installingExtensions.forEach(promise => promise.cancel());
this.uninstallingExtensions.forEach(promise => promise.cancel());
this.installingExtensions.clear();
this.uninstallingExtensions.clear();
}));
}
install(zipPath: string): TPromise<ILocalExtension> {
@ -208,7 +215,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<ILocalExtension> {
return this.getInstalled()
return this.toNonCancellablePromise(this.getInstalled()
.then(installed => {
const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed);
return this.installExtension({ zipPath, id: identifier.id, metadata })
@ -230,12 +237,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
local => { this._onDidInstallExtension.fire({ identifier, zipPath, local, operation }); return local; },
error => { this._onDidInstallExtension.fire({ identifier, zipPath, operation, error }); return TPromise.wrapError(error); }
);
});
}));
}
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
this.onInstallExtensions([extension]);
return this.getInstalled(LocalExtensionType.User)
return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User)
.then(installed => this.collectExtensionsToInstall(extension)
.then(
extensionsToInstall => {
@ -249,7 +256,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier))[0]),
errors => this.onDidInstallExtensions(extensionsToInstall, [], operataions, errors));
},
error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension.identifier, installed)], [error])));
error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension.identifier, installed)], [error]))));
}
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
@ -430,7 +437,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> {
const tempPath = path.join(this.extensionsPath, `.${id}`);
const extensionPath = path.join(this.extensionsPath, id);
return this.extractAndRename(id, zipPath, tempPath, extensionPath)
return pfs.rimraf(extensionPath)
.then(() => this.extractAndRename(id, zipPath, tempPath, extensionPath), e => TPromise.wrapError(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, id), INSTALL_ERROR_DELETING)))
.then(() => {
this.logService.info('Installation completed.', id);
return this.scanExtension(id, this.extensionsPath, LocalExtensionType.User);
@ -487,13 +495,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
uninstall(extension: ILocalExtension, force = false): TPromise<void> {
return this.getInstalled(LocalExtensionType.User)
return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User)
.then(installed => {
const promises = installed
.filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name)
.map(e => this.checkForDependenciesAndUninstall(e, installed, force));
return TPromise.join(promises).then(() => null, error => TPromise.wrapError(this.joinErrors(error)));
});
}));
}
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
@ -667,9 +675,16 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private uninstallExtension(local: ILocalExtension): TPromise<void> {
// Set all versions of the extension as uninstalled
return this.scanUserExtensions(false)
.then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id: getGalleryExtensionIdFromLocal(local), uuid: local.identifier.uuid }))));
const id = getGalleryExtensionIdFromLocal(local);
let promise = this.uninstallingExtensions.get(id);
if (!promise) {
// Set all versions of the extension as uninstalled
promise = this.scanUserExtensions(false)
.then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id, uuid: local.identifier.uuid }))))
.then(() => { this.uninstallingExtensions.delete(id); });
this.uninstallingExtensions.set(id, promise);
}
return promise;
}
private async postUninstallExtension(extension: ILocalExtension, error?: Error): TPromise<void> {
@ -863,6 +878,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
});
}
private toNonCancellablePromise<T>(promise: TPromise<T>): TPromise<T> {
return new TPromise((c, e) => promise.then(result => c(result), error => e(error)), () => this.logService.debug('Request Cancelled'));
}
private reportTelemetry(eventName: string, extensionData: any, duration: number, error?: Error): void {
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
/* __GDPR__

View file

@ -128,10 +128,11 @@ export interface IFileService {
createFolder(resource: URI): TPromise<IFileStat>;
/**
* Deletes the provided file. The optional useTrash parameter allows to
* move the file to trash.
* Deletes the provided file. The optional useTrash parameter allows to
* move the file to trash. The optional recursive parameter allows to delete
* non-empty folders recursively.
*/
del(resource: URI, useTrash?: boolean): TPromise<void>;
del(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): TPromise<void>;
/**
* Allows to start a watcher that reports file change events on the provided resource.
@ -158,6 +159,10 @@ export interface FileWriteOptions {
create: boolean;
}
export interface FileDeleteOptions {
recursive: boolean;
}
export enum FileType {
Unknown = 0,
File = 1,
@ -196,7 +201,7 @@ export interface IFileSystemProvider {
stat(resource: URI): TPromise<IStat>;
mkdir(resource: URI): TPromise<void>;
readdir(resource: URI): TPromise<[string, FileType][]>;
delete(resource: URI): TPromise<void>;
delete(resource: URI, opts: FileDeleteOptions): TPromise<void>;
rename(from: URI, to: URI, opts: FileOverwriteOptions): TPromise<void>;
copy?(from: URI, to: URI, opts: FileOverwriteOptions): TPromise<void>;

View file

@ -9,9 +9,9 @@ import { localize } from 'vs/nls';
// So that they are available for VS Code to use without downloading the entire language pack.
export const minimumTranslatedStrings = {
showLanguagePackExtensions: localize('showLanguagePackExtensions', "VS Code is available in {0}. Search for language packs in the Marketplace to get started."),
showLanguagePackExtensions: localize('showLanguagePackExtensions', "Search language packs in the Marketplace to change the display language to {0}."),
searchMarketplace: localize('searchMarketplace', "Search Marketplace"),
installAndRestartMessage: localize('installAndRestartMessage', "VS Code is available in {0}. Please install the language pack to change the display language."),
installAndRestartMessage: localize('installAndRestartMessage', "Install language pack to change the display language to {0}."),
installAndRestart: localize('installAndRestart', "Install and Restart")
};

View file

@ -22,6 +22,7 @@ export interface IProductConfiguration {
commit?: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;
experimentsUrl?: string;
date: string;
extensionsGallery: {
serviceUrl: string;

View file

@ -27,7 +27,7 @@ export interface ISearchService {
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem>;
extendQuery(query: ISearchQuery): void;
clearCache(cacheKey: string): TPromise<void>;
registerSearchResultProvider(provider: ISearchResultProvider): IDisposable;
registerSearchResultProvider(scheme: string, provider: ISearchResultProvider): IDisposable;
}
export interface ISearchHistoryValues {

View file

@ -1,92 +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 { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { deepClone } from 'vs/base/common/objects';
/* __GDPR__FRAGMENT__
"IExperiments" : {
}
*/
export interface IExperiments {
}
export const IExperimentService = createDecorator<IExperimentService>('experimentService');
export interface IExperimentService {
_serviceBrand: any;
getExperiments(): IExperiments;
}
export class ExperimentService implements IExperimentService {
_serviceBrand: any;
private experiments: IExperiments = {}; // Shortcut while there are no experiments.
constructor(
@IStorageService private storageService: IStorageService,
@IConfigurationService private configurationService: IConfigurationService,
) { }
getExperiments() {
if (!this.experiments) {
this.experiments = loadExperiments(this.storageService, this.configurationService);
}
return this.experiments;
}
}
function loadExperiments(storageService: IStorageService, configurationService: IConfigurationService): IExperiments {
const experiments = splitExperimentsRandomness(storageService);
return applyOverrides(experiments, configurationService);
}
function applyOverrides(experiments: IExperiments, configurationService: IConfigurationService): IExperiments {
const experimentsConfig = getExperimentsOverrides(configurationService);
Object.keys(experiments).forEach(key => {
if (key in experimentsConfig) {
experiments[key] = experimentsConfig[key];
}
});
return experiments;
}
function splitExperimentsRandomness(storageService: IStorageService): IExperiments {
const random1 = getExperimentsRandomness(storageService);
const [/* random2 */, /* ripgrepQuickSearch */] = splitRandom(random1);
// const [/* random3 */, /* deployToAzureQuickLink */] = splitRandom(random2);
// const [random4, /* mergeQuickLinks */] = splitRandom(random3);
// const [random5, /* enableWelcomePage */] = splitRandom(random4);
return {
// ripgrepQuickSearch,
};
}
function getExperimentsRandomness(storageService: IStorageService) {
const key = 'experiments.randomness';
let valueString = storageService.get(key);
if (!valueString) {
valueString = Math.random().toString();
storageService.store(key, valueString);
}
return parseFloat(valueString);
}
function splitRandom(random: number): [number, boolean] {
const scaled = random * 2;
const i = Math.floor(scaled);
return [scaled - i, i === 1];
}
function getExperimentsOverrides(configurationService: IConfigurationService): IExperiments {
return deepClone(configurationService.getValue<any>('experiments')) || {};
}

View file

@ -89,4 +89,6 @@ export interface IUpdateService {
downloadUpdate(): TPromise<void>;
applyUpdate(): TPromise<void>;
quitAndInstall(): TPromise<void>;
}
isLatestVersion(): TPromise<boolean | undefined>;
}

View file

@ -17,6 +17,7 @@ export interface IUpdateChannel extends IChannel {
call(command: 'applyUpdate'): TPromise<void>;
call(command: 'quitAndInstall'): TPromise<void>;
call(command: '_getInitialState'): TPromise<State>;
call(command: 'isLatestVersion'): TPromise<boolean>;
call(command: string, arg?: any): TPromise<any>;
}
@ -32,6 +33,7 @@ export class UpdateChannel implements IUpdateChannel {
case 'applyUpdate': return this.service.applyUpdate();
case 'quitAndInstall': return this.service.quitAndInstall();
case '_getInitialState': return TPromise.as(this.service.state);
case 'isLatestVersion': return this.service.isLatestVersion();
}
return undefined;
}
@ -77,4 +79,8 @@ export class UpdateChannelClient implements IUpdateService {
quitAndInstall(): TPromise<void> {
return this.channel.call('quitAndInstall');
}
}
isLatestVersion(): TPromise<boolean> {
return this.channel.call('isLatestVersion');
}
}

View file

@ -14,6 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IUpdateService, State, StateType, AvailableForDownload } from 'vs/platform/update/common/update';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { IRequestService } from 'vs/platform/request/node/request';
export function createUpdateURL(platform: string, quality: string): string {
return `${product.updateUrl}/api/update/${platform}/${quality}/${product.commit}`;
@ -23,6 +24,8 @@ export abstract class AbstractUpdateService implements IUpdateService {
_serviceBrand: any;
protected readonly url: string | undefined;
private _state: State = State.Uninitialized;
private throttler: Throttler = new Throttler();
@ -43,7 +46,8 @@ export abstract class AbstractUpdateService implements IUpdateService {
@ILifecycleService private lifecycleService: ILifecycleService,
@IConfigurationService protected configurationService: IConfigurationService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILogService protected logService: ILogService
@IRequestService protected requestService: IRequestService,
@ILogService protected logService: ILogService,
) {
if (this.environmentService.disableUpdates) {
this.logService.info('update#ctor - updates are disabled');
@ -62,7 +66,8 @@ export abstract class AbstractUpdateService implements IUpdateService {
return;
}
if (!this.setUpdateFeedUrl(quality)) {
this.url = this.buildUpdateFeedUrl(quality);
if (!this.url) {
this.logService.info('update#ctor - updates are disabled');
return;
}
@ -153,10 +158,25 @@ export abstract class AbstractUpdateService implements IUpdateService {
return TPromise.as(null);
}
isLatestVersion(): TPromise<boolean | undefined> {
if (!this.url) {
return TPromise.as(undefined);
}
return this.requestService.request({ url: this.url }).then(context => {
// The update server replies with 204 (No Content) when no
// update is available - that's all we want to know.
if (context.res.statusCode === 204) {
return true;
} else {
return false;
}
});
}
protected doQuitAndInstall(): void {
// noop
}
protected abstract setUpdateFeedUrl(quality: string): boolean;
protected abstract buildUpdateFeedUrl(quality: string): string | undefined;
protected abstract doCheckForUpdates(context: any): void;
}

View file

@ -16,6 +16,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { AbstractUpdateService, createUpdateURL } from 'vs/platform/update/electron-main/abstractUpdateService';
import { IRequestService } from 'vs/platform/request/node/request';
export class DarwinUpdateService extends AbstractUpdateService {
@ -33,9 +34,10 @@ export class DarwinUpdateService extends AbstractUpdateService {
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
super(lifecycleService, configurationService, environmentService, requestService, logService);
this.onRawError(this.onError, this, this.disposables);
this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables);
this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables);
@ -47,16 +49,16 @@ export class DarwinUpdateService extends AbstractUpdateService {
this.setState(State.Idle);
}
protected setUpdateFeedUrl(quality: string): boolean {
protected buildUpdateFeedUrl(quality: string): string | undefined {
const url = createUpdateURL('darwin', quality);
try {
electron.autoUpdater.setFeedURL({ url: createUpdateURL('darwin', quality) });
electron.autoUpdater.setFeedURL({ url });
} catch (e) {
// application is very likely not signed
this.logService.error('Failed to set update feed URL', e);
return false;
return undefined;
}
return true;
return url;
}
protected doCheckForUpdates(context: any): void {

View file

@ -22,22 +22,19 @@ export class LinuxUpdateService extends AbstractUpdateService {
_serviceBrand: any;
private url: string | undefined;
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService private requestService: IRequestService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
super(lifecycleService, configurationService, environmentService, requestService, logService);
}
protected setUpdateFeedUrl(quality: string): boolean {
this.url = createUpdateURL(`linux-${process.arch}`, quality);
return true;
protected buildUpdateFeedUrl(quality: string): string {
return createUpdateURL(`linux-${process.arch}`, quality);
}
protected doCheckForUpdates(context: any): void {
@ -84,8 +81,8 @@ export class LinuxUpdateService extends AbstractUpdateService {
} else {
shell.openExternal(state.update.url);
}
this.setState(State.Idle);
this.setState(State.Idle);
return TPromise.as(null);
}
}

View file

@ -14,7 +14,7 @@ import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycle
import { IRequestService } from 'vs/platform/request/node/request';
import product from 'vs/platform/node/product';
import { TPromise, Promise } from 'vs/base/common/winjs.base';
import { State, IUpdate, StateType } from 'vs/platform/update/common/update';
import { State, IUpdate, StateType, AvailableForDownload } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
@ -23,6 +23,7 @@ import { download, asJson } from 'vs/base/node/request';
import { checksum } from 'vs/base/node/crypto';
import { tmpdir } from 'os';
import { spawn } from 'child_process';
import { shell } from 'electron';
function pollUntil(fn: () => boolean, timeout = 1000): TPromise<void> {
return new TPromise<void>(c => {
@ -43,11 +44,26 @@ interface IAvailableUpdate {
updateFilePath?: string;
}
enum UpdateType {
Automatic,
Manual
}
let _updateType: UpdateType | undefined = undefined;
function getUpdateType(): UpdateType {
if (typeof _updateType === 'undefined') {
_updateType = fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))
? UpdateType.Automatic
: UpdateType.Manual;
}
return _updateType;
}
export class Win32UpdateService extends AbstractUpdateService {
_serviceBrand: any;
private url: string | undefined;
private availableUpdate: IAvailableUpdate | undefined;
@memoize
@ -61,29 +77,26 @@ export class Win32UpdateService extends AbstractUpdateService {
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService private requestService: IRequestService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
super(lifecycleService, configurationService, environmentService, requestService, logService);
}
protected setUpdateFeedUrl(quality: string): boolean {
if (!fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))) {
return false;
}
protected buildUpdateFeedUrl(quality: string): string | undefined {
let platform = 'win32';
if (process.arch === 'x64') {
platform += '-x64';
}
if (product.target === 'user') {
if (getUpdateType() === UpdateType.Manual) {
platform += '-archive';
} else if (product.target === 'user') {
platform += '-user';
}
this.url = createUpdateURL(platform, quality);
return true;
return createUpdateURL(platform, quality);
}
protected doCheckForUpdates(context: any): void {
@ -96,7 +109,7 @@ export class Win32UpdateService extends AbstractUpdateService {
this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
.then(update => {
if (!update || !update.url || !update.version) {
if (!update || !update.url || !update.version || !update.productVersion) {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
@ -108,6 +121,11 @@ export class Win32UpdateService extends AbstractUpdateService {
return TPromise.as(null);
}
if (getUpdateType() === UpdateType.Manual) {
this.setState(State.AvailableForDownload(update));
return TPromise.as(null);
}
this.setState(State.Downloading(update));
return this.cleanup(update.version).then(() => {
@ -156,6 +174,12 @@ export class Win32UpdateService extends AbstractUpdateService {
});
}
protected doDownloadUpdate(state: AvailableForDownload): TPromise<void> {
shell.openExternal(state.update.url);
this.setState(State.Idle);
return TPromise.as(null);
}
private getUpdatePackagePath(version: string): TPromise<string> {
return this.cachePath.then(cachePath => path.join(cachePath, `CodeSetup-${product.quality}-${version}.exe`));
}

View file

@ -105,6 +105,7 @@ export interface IWindowsService {
onWindowBlur: Event<number>;
onWindowMaximize: Event<number>;
onWindowUnmaximize: Event<number>;
onRecentlyOpenedChange: Event<void>;
// Dialogs
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void>;
@ -169,6 +170,7 @@ export interface IWindowsService {
// TODO: this is a bit backwards
startCrashReporter(config: CrashReporterStartOptions): TPromise<void>;
openAccessibilityOptions(): TPromise<void>;
openAboutDialog(): TPromise<void>;
}

View file

@ -19,6 +19,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'event:onWindowOpen'): TPromise<number>;
call(command: 'event:onWindowFocus'): TPromise<number>;
call(command: 'event:onWindowBlur'): TPromise<number>;
call(command: 'event:onRecentlyOpenedChange'): TPromise<void>;
call(command: 'pickFileFolderAndOpen', arg: INativeOpenDialogOptions): TPromise<void>;
call(command: 'pickFileAndOpen', arg: INativeOpenDialogOptions): TPromise<void>;
call(command: 'pickFolderAndOpen', arg: INativeOpenDialogOptions): TPromise<void>;
@ -65,6 +66,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'showItemInFolder', arg: string): TPromise<void>;
call(command: 'openExternal', arg: string): TPromise<boolean>;
call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise<void>;
call(command: 'openAccessibilityOptions'): TPromise<void>;
call(command: 'openAboutDialog'): TPromise<void>;
call(command: string, arg?: any): TPromise<any>;
}
@ -76,6 +78,7 @@ export class WindowsChannel implements IWindowsChannel {
private onWindowBlur: Event<number>;
private onWindowMaximize: Event<number>;
private onWindowUnmaximize: Event<number>;
private onRecentlyOpenedChange: Event<void>;
constructor(private service: IWindowsService) {
this.onWindowOpen = buffer(service.onWindowOpen, true);
@ -83,6 +86,7 @@ export class WindowsChannel implements IWindowsChannel {
this.onWindowBlur = buffer(service.onWindowBlur, true);
this.onWindowMaximize = buffer(service.onWindowMaximize, true);
this.onWindowUnmaximize = buffer(service.onWindowUnmaximize, true);
this.onRecentlyOpenedChange = buffer(service.onRecentlyOpenedChange, true);
}
call(command: string, arg?: any): TPromise<any> {
@ -92,6 +96,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'event:onWindowBlur': return eventToCall(this.onWindowBlur);
case 'event:onWindowMaximize': return eventToCall(this.onWindowMaximize);
case 'event:onWindowUnmaximize': return eventToCall(this.onWindowUnmaximize);
case 'event:onRecentlyOpenedChange': return eventToCall(this.onRecentlyOpenedChange);
case 'pickFileFolderAndOpen': return this.service.pickFileFolderAndOpen(arg);
case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg);
case 'pickFolderAndOpen': return this.service.pickFolderAndOpen(arg);
@ -152,6 +157,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'showItemInFolder': return this.service.showItemInFolder(arg);
case 'openExternal': return this.service.openExternal(arg);
case 'startCrashReporter': return this.service.startCrashReporter(arg);
case 'openAccessibilityOptions': return this.service.openAccessibilityOptions();
case 'openAboutDialog': return this.service.openAboutDialog();
}
return undefined;
@ -179,6 +185,9 @@ export class WindowsChannelClient implements IWindowsService {
private _onWindowUnmaximize: Event<number> = eventFromCall<number>(this.channel, 'event:onWindowUnmaximize');
get onWindowUnmaximize(): Event<number> { return this._onWindowUnmaximize; }
private _onRecentlyOpenedChange: Event<void> = eventFromCall<void>(this.channel, 'event:onRecentlyOpenedChange');
get onRecentlyOpenedChange(): Event<void> { return this._onRecentlyOpenedChange; }
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
return this.channel.call('pickFileFolderAndOpen', options);
}
@ -367,6 +376,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('updateTouchBar', [windowId, items]);
}
openAccessibilityOptions(): TPromise<void> {
return this.channel.call('openAccessibilityOptions');
}
openAboutDialog(): TPromise<void> {
return this.channel.call('openAboutDialog');
}

View file

@ -13,7 +13,7 @@ import URI from 'vs/base/common/uri';
import product from 'vs/platform/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions } from 'vs/platform/windows/common/windows';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard } from 'electron';
import { shell, crashReporter, app, Menu, clipboard, BrowserWindow } from 'electron';
import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
@ -42,6 +42,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
readonly onWindowMaximize: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-maximize', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onWindowUnmaximize: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-unmaximize', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onRecentlyOpenedChange: Event<void> = this.historyService.onRecentlyOpenedChange;
constructor(
private sharedProcess: ISharedProcess,
@IWindowsMainService private windowsMainService: IWindowsMainService,
@ -468,6 +470,29 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(null);
}
openAccessibilityOptions(): TPromise<void> {
this.logService.trace('windowsService#openAccessibilityOptions');
const win = new BrowserWindow({
alwaysOnTop: true,
skipTaskbar: true,
resizable: false,
width: 450,
height: 300,
show: true,
title: nls.localize('accessibilityOptionsWindowTitle', "Accessibility Options"),
webPreferences: {
disableBlinkFeatures: 'Auxclick'
}
});
win.setMenuBarVisibility(false);
win.loadURL('chrome://accessibility');
return TPromise.as(null);
}
openAboutDialog(): TPromise<void> {
this.logService.trace('windowsService#openAboutDialog');
const lastActiveWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();

View file

@ -96,12 +96,7 @@ export class WorkspacesMainService implements IWorkspacesMainService {
private doParseStoredWorkspace(path: string, contents: string): IStoredWorkspace {
// Parse workspace file
let storedWorkspace: IStoredWorkspace;
try {
storedWorkspace = json.parse(contents); // use fault tolerant parser
} catch (error) {
throw new Error(`${path} cannot be parsed as JSON file (${error}).`);
}
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
// Filter out folders which do not have a path or uri set
if (Array.isArray(storedWorkspace.folders)) {

Some files were not shown because too many files have changed in this diff Show more