Merge branch 'master' into electron-2.0.x
This commit is contained in:
commit
59c29b42db
86
.github/classifier.yml
vendored
86
.github/classifier.yml
vendored
|
@ -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 ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
41
extensions/json/build/update-grammars.js
Normal file
41
extensions/json/build/update-grammars.js
Normal 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'));
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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": [
|
||||
|
|
213
extensions/json/syntaxes/JSONC.tmLanguage.json
Normal file
213
extensions/json/syntaxes/JSONC.tmLanguage.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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.",
|
||||
|
|
|
@ -290,7 +290,9 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.type.property-name.json",
|
||||
"scope": [
|
||||
"support.type.property-name.json"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
14
package.json
14
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/cli.js
16
src/cli.js
|
@ -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);
|
||||
|
|
16
src/main.js
16
src/main.js
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
.monaco-action-bar .action-label {
|
||||
font-size: 11px;
|
||||
margin-right: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-label.octicon {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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}`));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
7
src/vs/monaco.d.ts
vendored
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ export interface IConfigurationNode {
|
|||
allOf?: IConfigurationNode[];
|
||||
overridable?: boolean;
|
||||
scope?: ConfigurationScope;
|
||||
contributedByExtension?: boolean;
|
||||
}
|
||||
|
||||
export interface IDefaultConfigurationExtension {
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface IProductConfiguration {
|
|||
commit?: string;
|
||||
settingsSearchBuildId?: number;
|
||||
settingsSearchUrl?: string;
|
||||
experimentsUrl?: string;
|
||||
date: string;
|
||||
extensionsGallery: {
|
||||
serviceUrl: string;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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')) || {};
|
||||
}
|
|
@ -89,4 +89,6 @@ export interface IUpdateService {
|
|||
downloadUpdate(): TPromise<void>;
|
||||
applyUpdate(): TPromise<void>;
|
||||
quitAndInstall(): TPromise<void>;
|
||||
}
|
||||
|
||||
isLatestVersion(): TPromise<boolean | undefined>;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`));
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue