diff --git a/build/package.json b/build/package.json
index 50b8a85f39..8fe67192a0 100644
--- a/build/package.json
+++ b/build/package.json
@@ -43,7 +43,7 @@
"request": "^2.85.0",
"tslint": "^5.9.1",
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
- "typescript": "3.4.1",
+ "typescript": "3.4.5",
"vsce": "1.48.0",
"xml2js": "^0.4.17"
},
diff --git a/build/yarn.lock b/build/yarn.lock
index c1f72ff764..7a77cf1451 100644
--- a/build/yarn.lock
+++ b/build/yarn.lock
@@ -3201,10 +3201,10 @@ typed-rest-client@^0.9.0:
tunnel "0.0.4"
underscore "1.8.3"
-typescript@3.4.1:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
- integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
+typescript@3.4.5:
+ version "3.4.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
+ integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.5"
diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json
index b204fb78f5..e18099fe67 100644
--- a/extensions/markdown-language-features/package.json
+++ b/extensions/markdown-language-features/package.json
@@ -10,7 +10,6 @@
"vscode": "^1.20.0"
},
"main": "./out/extension",
- "extensionKind": "ui",
"categories": [
"Programming Languages"
],
diff --git a/extensions/package.json b/extensions/package.json
index 75b3235c7c..8d41ef689c 100644
--- a/extensions/package.json
+++ b/extensions/package.json
@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
- "typescript": "3.4.4"
+ "typescript": "3.4.5"
},
"scripts": {
"postinstall": "node ./postinstall"
diff --git a/extensions/yarn.lock b/extensions/yarn.lock
index 4357f5258d..149346bbb9 100644
--- a/extensions/yarn.lock
+++ b/extensions/yarn.lock
@@ -2,7 +2,7 @@
# yarn lockfile v1
-typescript@3.4.4:
- version "3.4.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.4.tgz#aac4a08abecab8091a75f10842ffa0631818f785"
- integrity sha512-xt5RsIRCEaf6+j9AyOBgvVuAec0i92rgCaS3S+UVf5Z/vF2Hvtsw08wtUTJqp4djwznoAgjSxeCcU4r+CcDBJA==
+typescript@3.4.5:
+ version "3.4.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
+ integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
diff --git a/package.json b/package.json
index 228214e4b5..2a2be6a95b 100644
--- a/package.json
+++ b/package.json
@@ -158,7 +158,7 @@
"tslint": "^5.11.0",
"tslint-microsoft-contrib": "^6.0.0",
"typemoq": "^0.3.2",
- "typescript": "3.4.1",
+ "typescript": "3.4.5",
"typescript-formatter": "7.1.0",
"typescript-tslint-plugin": "^0.0.7",
"uglify-es": "^3.0.18",
diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh
index 75ee9c493c..a4a621417e 100644
--- a/resources/win32/bin/code.sh
+++ b/resources/win32/bin/code.sh
@@ -6,29 +6,49 @@ COMMIT="@@COMMIT@@"
APP_NAME="@@APPNAME@@"
QUALITY="@@QUALITY@@"
NAME="@@NAME@@"
-
+VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
+ELECTRON="$VSCODE_PATH/$NAME.exe"
if grep -qi Microsoft /proc/version; then
# in a wsl shell
- WIN_CODE_CMD=$(wslpath -w "$(dirname "$(realpath "$0")")/$APP_NAME.cmd")
- if ! [ -z "$WIN_CODE_CMD" ]; then
+ fallback() {
+ # If running under older WSL, don't pass cli.js to Electron as
+ # environment vars cannot be transferred from WSL to Windows
+ # See: https://github.com/Microsoft/BashOnWindows/issues/1363
+ # https://github.com/Microsoft/BashOnWindows/issues/1494
+ "$ELECTRON" "$@"
+ exit $?
+ }
+ WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/')
+ # wslpath is not available prior to WSL build 17046
+ # See: https://docs.microsoft.com/en-us/windows/wsl/release-notes#build-17046
+ if [ -x /bin/wslpath ]; then
+ WIN_CODE_CMD=$(wslpath -w "$(dirname "$(realpath "$0")")/$APP_NAME.cmd")
+ # make sure the cwd is in the windows fs, otherwise there will be a warning from cmd
+ pushd "$(dirname "$0")" > /dev/null
WSL_EXT_ID="ms-vscode.remote-wsl"
WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CMD" --locate-extension $WSL_EXT_ID)
+ popd > /dev/null
if ! [ -z "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC, get linux path for
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh
"$WSL_CODE" $COMMIT $QUALITY "$WIN_CODE_CMD" "$APP_NAME" "$@"
exit $?
+ elif [ $WSL_BUILD -ge 17063 ] 2> /dev/null; then
+ # Since WSL build 17063, we just need to set WSLENV so that
+ # ELECTRON_RUN_AS_NODE is visible to the win32 process
+ # See: https://docs.microsoft.com/en-us/windows/wsl/release-notes#build-17063
+ export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV
+ CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js")
+ else # $WSL_BUILD ∈ [17046, 17063) OR $WSL_BUILD is indeterminate
+ fallback "$@"
fi
+ else
+ fallback "$@"
fi
-fi
-
-VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
-
-if [ -x "$(command -v cygpath)" ]; then
+elif [ -x "$(command -v cygpath)" ]; then
CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js")
else
CLI="$VSCODE_PATH/resources/app/out/cli.js"
fi
-ELECTRON="$VSCODE_PATH/$NAME.exe"
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"
exit $?
diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts
index b964e33fb8..92793eb2a5 100644
--- a/src/vs/base/browser/ui/dialog/dialog.ts
+++ b/src/vs/base/browser/ui/dialog/dialog.ts
@@ -40,7 +40,6 @@ export class Dialog extends Disposable {
private buttonGroup: ButtonGroup | undefined;
private styles: IDialogStyles | undefined;
private focusToReturn: HTMLElement | undefined;
- private iconRotatingInternal: any | undefined;
constructor(private container: HTMLElement, private message: string, private buttons: string[], private options: IDialogOptions) {
super();
@@ -163,15 +162,6 @@ export class Dialog extends Disposable {
break;
case 'pending':
addClass(this.iconElement, 'icon-pending');
- let deg = 0;
- this.iconRotatingInternal = setInterval(() => {
- if (this.iconElement) {
- this.iconElement.style.transform = `rotate(${deg}deg)`;
- deg += 45; // 360 / 8
- } else {
- this.iconRotatingInternal = undefined;
- }
- }, 125 /** 1000 / 8 */);
break;
case 'none':
case 'info':
@@ -233,10 +223,6 @@ export class Dialog extends Disposable {
this.modal = undefined;
}
- if (this.iconRotatingInternal) {
- this.iconRotatingInternal = undefined;
- }
-
if (this.focusToReturn && isAncestor(this.focusToReturn, document.body)) {
this.focusToReturn.focus();
this.focusToReturn = undefined;
diff --git a/src/vs/base/browser/ui/dialog/pending-dark.svg b/src/vs/base/browser/ui/dialog/pending-dark.svg
index 97810808c3..5f38838116 100644
--- a/src/vs/base/browser/ui/dialog/pending-dark.svg
+++ b/src/vs/base/browser/ui/dialog/pending-dark.svg
@@ -1,13 +1,31 @@
diff --git a/src/vs/base/browser/ui/dialog/pending-hc.svg b/src/vs/base/browser/ui/dialog/pending-hc.svg
index 73c63ba3ce..c6d0ec7e29 100644
--- a/src/vs/base/browser/ui/dialog/pending-hc.svg
+++ b/src/vs/base/browser/ui/dialog/pending-hc.svg
@@ -1,13 +1,31 @@
diff --git a/src/vs/base/browser/ui/dialog/pending.svg b/src/vs/base/browser/ui/dialog/pending.svg
index 113a96cfcf..47ce444bb2 100644
--- a/src/vs/base/browser/ui/dialog/pending.svg
+++ b/src/vs/base/browser/ui/dialog/pending.svg
@@ -1,13 +1,31 @@
diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json
index b0c1ce8f3d..f3477931d1 100644
--- a/src/vs/base/common/marked/cgmanifest.json
+++ b/src/vs/base/common/marked/cgmanifest.json
@@ -6,11 +6,11 @@
"git": {
"name": "marked",
"repositoryUrl": "https://github.com/markedjs/marked",
- "commitHash": "78c977bc3a47f9e2fb146477d1ca3dad0cb134e6"
+ "commitHash": "529a8d4e185a8aa561e4d8d2891f8556b5717cd4"
}
},
"license": "MIT",
- "version": "0.5.0"
+ "version": "0.6.2"
}
],
"version": 1
diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js
index 1e63f272db..968f9029e9 100644
--- a/src/vs/base/common/marked/marked.js
+++ b/src/vs/base/common/marked/marked.js
@@ -23,7 +23,7 @@ var block = {
heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
nptable: noop,
blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
- list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+ list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
html: '^ {0,3}(?:' // optional indentation
+ '<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)' // (1)
+ '|comment[^\\n]*(\\n+|$)' // (2)
@@ -31,8 +31,8 @@ var block = {
+ '|\\n*' // (4)
+ '|\\n*' // (5)
+ '|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
- + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
- + '|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
+ + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
+ + '|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
+ ')',
def: /^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
table: noop,
@@ -48,8 +48,8 @@ block.def = edit(block.def)
.replace('title', block._title)
.getRegex();
-block.bullet = /(?:[*+-]|\d+\.)/;
-block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.bullet = /(?:[*+-]|\d{1,9}\.)/;
+block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
block.item = edit(block.item, 'gm')
.replace(/bull/g, block.bullet)
.getRegex();
@@ -95,7 +95,7 @@ block.normal = merge({}, block);
*/
block.gfm = merge({}, block.normal, {
- fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,
+ fences: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
paragraph: /^/,
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
});
@@ -235,7 +235,7 @@ Lexer.prototype.token = function(src, top) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'code',
- lang: cap[2],
+ lang: cap[2] ? cap[2].trim() : cap[2],
text: cap[3] || ''
});
continue;
@@ -253,7 +253,7 @@ Lexer.prototype.token = function(src, top) {
}
// table no leading pipe (gfm)
- if (top && (cap = this.rules.nptable.exec(src))) {
+ if (cap = this.rules.nptable.exec(src)) {
item = {
type: 'table',
header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
@@ -346,7 +346,7 @@ Lexer.prototype.token = function(src, top) {
// Remove the list item's bullet
// so it is seen as the next token.
space = item.length;
- item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+ item = item.replace(/^ *([*+-]|\d+\.) */, '');
// Outdent whatever the
// list item contains. Hacky.
@@ -359,9 +359,10 @@ Lexer.prototype.token = function(src, top) {
// Determine whether the next list item belongs here.
// Backpedal if it does not belong in this list.
- if (this.options.smartLists && i !== l - 1) {
+ if (i !== l - 1) {
b = block.bullet.exec(cap[i + 1])[0];
- if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+ if (bull.length > 1 ? b.length === 1
+ : (b.length > 1 || (this.options.smartLists && b !== bull))) {
src = cap.slice(i + 1).join('\n') + src;
i = l - 1;
}
@@ -450,12 +451,12 @@ Lexer.prototype.token = function(src, top) {
}
// table (gfm)
- if (top && (cap = this.rules.table.exec(src))) {
+ if (cap = this.rules.table.exec(src)) {
item = {
type: 'table',
header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
- cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
+ cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
};
if (item.header.length === item.align.length) {
@@ -544,14 +545,19 @@ var inline = {
link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
- strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
- em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
- code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
+ strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
+ em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
+ code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
br: /^( {2,}|\\)\n(?!\s*$)/,
del: noop,
- text: /^[\s\S]+?(?=[\\?@\\[^_{|}~';
+inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
+
inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
@@ -568,8 +574,8 @@ inline.tag = edit(inline.tag)
.replace('attribute', inline._attribute)
.getRegex();
-inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
-inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
+inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/;
+inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/;
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
inline.link = edit(inline.link)
@@ -609,24 +615,23 @@ inline.pedantic = merge({}, inline.normal, {
inline.gfm = merge({}, inline.normal, {
escape: edit(inline.escape).replace('])', '~|])').getRegex(),
- url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
- .replace('email', inline._email)
- .getRegex(),
+ _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
+ url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
_backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
del: /^~+(?=\S)([\s\S]*?\S)~+/,
- text: edit(inline.text)
- .replace(']|', '~]|')
- .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
- .getRegex()
+ text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(cap[0])) {
this.inLink = false;
}
+ if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
+ this.inRawBlock = true;
+ } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
+ this.inRawBlock = false;
+ }
+
src = src.substring(cap[0].length);
out += this.options.sanitize
? this.options.sanitizer
? this.options.sanitizer(cap[0])
: escape(cap[0])
- : cap[0]
+ : cap[0];
continue;
}
// link
if (cap = this.rules.link.exec(src)) {
+ var lastParenIndex = findClosingBracket(cap[2], '()');
+ if (lastParenIndex > -1) {
+ var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length;
+ cap[2] = cap[2].substring(0, lastParenIndex);
+ cap[0] = cap[0].substring(0, linkLen).trim();
+ cap[3] = '';
+ }
src = src.substring(cap[0].length);
this.inLink = true;
href = cap[2];
@@ -821,10 +803,51 @@ InlineLexer.prototype.output = function(src) {
continue;
}
+ // autolink
+ if (cap = this.rules.autolink.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = escape(this.mangle(cap[1]));
+ href = 'mailto:' + text;
+ } else {
+ text = escape(cap[1]);
+ href = text;
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // url (gfm)
+ if (!this.inLink && (cap = this.rules.url.exec(src))) {
+ if (cap[2] === '@') {
+ text = escape(cap[0]);
+ href = 'mailto:' + text;
+ } else {
+ // do extended autolink path validation
+ do {
+ prevCapZero = cap[0];
+ cap[0] = this.rules._backpedal.exec(cap[0])[0];
+ } while (prevCapZero !== cap[0]);
+ text = escape(cap[0]);
+ if (cap[1] === 'www.') {
+ href = 'http://' + text;
+ } else {
+ href = text;
+ }
+ }
+ src = src.substring(cap[0].length);
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
// text
if (cap = this.rules.text.exec(src)) {
src = src.substring(cap[0].length);
- out += this.renderer.text(escape(this.smartypants(cap[0])));
+ if (this.inRawBlock) {
+ out += this.renderer.text(cap[0]);
+ } else {
+ out += this.renderer.text(escape(this.smartypants(cap[0])));
+ }
continue;
}
@@ -838,7 +861,7 @@ InlineLexer.prototype.output = function(src) {
InlineLexer.escapes = function(text) {
return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
-}
+};
/**
* Compile Link
@@ -906,7 +929,8 @@ function Renderer(options) {
this.options = options || marked.defaults;
}
-Renderer.prototype.code = function(code, lang, escaped) {
+Renderer.prototype.code = function(code, infostring, escaped) {
+ var lang = (infostring || '').match(/\S*/)[0];
if (this.options.highlight) {
var out = this.options.highlight(code, lang);
if (out != null && out !== code) {
@@ -937,13 +961,13 @@ Renderer.prototype.html = function(html) {
return html;
};
-Renderer.prototype.heading = function(text, level, raw) {
+Renderer.prototype.heading = function(text, level, raw, slugger) {
if (this.options.headerIds) {
return ' ';
-}
+};
Renderer.prototype.paragraph = function(text) {
return '
' + text + '
\n';
@@ -1025,24 +1049,8 @@ Renderer.prototype.del = function(text) {
};
Renderer.prototype.link = function(href, title, text) {
- if (this.options.sanitize) {
- try {
- var prot = decodeURIComponent(unescape(href))
- .replace(/[^\w:]/g, '')
- .toLowerCase();
- } catch (e) {
- return text;
- }
- if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
- return text;
- }
- }
- if (this.options.baseUrl && !originIndependentUrl.test(href)) {
- href = resolveUrl(this.options.baseUrl, href);
- }
- try {
- href = encodeURI(href).replace(/%25/g, '%');
- } catch (e) {
+ href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
+ if (href === null) {
return text;
}
var out = '?@[\]^`{|}~]/g, '')
+ .replace(/\s/g, '-');
+
+ if (this.seen.hasOwnProperty(slug)) {
+ var originalSlug = slug;
+ do {
+ this.seen[originalSlug]++;
+ slug = originalSlug + '-' + this.seen[originalSlug];
+ } while (this.seen.hasOwnProperty(slug));
+ }
+ this.seen[slug] = 0;
+
+ return slug;
+};
+
/**
* Helpers
*/
function escape(html, encode) {
- return html
- .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
+ if (encode) {
+ if (escape.escapeTest.test(html)) {
+ return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; });
+ }
+ } else {
+ if (escape.escapeTestNoEncode.test(html)) {
+ return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; });
+ }
+ }
+
+ return html;
}
+escape.escapeTest = /[&<>"']/;
+escape.escapeReplace = /[&<>"']/g;
+escape.replacements = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+};
+
+escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
+escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
+
function unescape(html) {
// explicitly match decimal, hex, and named HTML entities
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
@@ -1316,6 +1386,30 @@ function edit(regex, opt) {
};
}
+function cleanUrl(sanitize, base, href) {
+ if (sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href))
+ .replace(/[^\w:]/g, '')
+ .toLowerCase();
+ } catch (e) {
+ return null;
+ }
+ if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
+ return null;
+ }
+ }
+ if (base && !originIndependentUrl.test(href)) {
+ href = resolveUrl(base, href);
+ }
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch (e) {
+ return null;
+ }
+ return href;
+}
+
function resolveUrl(base, href) {
if (!baseUrls[' ' + base]) {
// we can ignore everything in base after the last slash of its path component,
@@ -1418,6 +1512,26 @@ function rtrim(str, c, invert) {
return str.substr(0, str.length - suffLen);
}
+function findClosingBracket(str, b) {
+ if (str.indexOf(b[1]) === -1) {
+ return -1;
+ }
+ var level = 0;
+ for (var i = 0; i < str.length; i++) {
+ if (str[i] === '\\') {
+ i++;
+ } else if (str[i] === b[0]) {
+ level++;
+ } else if (str[i] === b[1]) {
+ level--;
+ if (level < 0) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
/**
* Marked
*/
@@ -1446,7 +1560,7 @@ function marked(src, opt, callback) {
i = 0;
try {
- tokens = Lexer.lex(src, opt)
+ tokens = Lexer.lex(src, opt);
} catch (e) {
return callback(e);
}
@@ -1545,7 +1659,7 @@ marked.getDefaults = function () {
tables: true,
xhtml: false
};
-}
+};
marked.defaults = marked.getDefaults();
@@ -1565,6 +1679,8 @@ marked.lexer = Lexer.lex;
marked.InlineLexer = InlineLexer;
marked.inlineLexer = InlineLexer.output;
+marked.Slugger = Slugger;
+
marked.parse = marked;
// BEGIN MONACOCHANGE
@@ -1582,7 +1698,7 @@ __marked_exports = marked;
// ESM-comment-begin
define(function() { return __marked_exports; });
// ESM-comment-end
-
+
// ESM-uncomment-begin
// export var marked = __marked_exports;
// export var Parser = __marked_exports.Parser;
diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts
index dde4a358fd..d4888386ec 100644
--- a/src/vs/base/common/resources.ts
+++ b/src/vs/base/common/resources.ts
@@ -146,7 +146,7 @@ export function normalizePath(resource: URI): URI {
export function originalFSPath(uri: URI): string {
let value: string;
const uriPath = uri.path;
- if (uri.authority && uriPath.length > 1 && uri.scheme === 'file') {
+ if (uri.authority && uriPath.length > 1 && uri.scheme === Schemas.file) {
// unc path: file://shares/c$/far/boo
value = `//${uri.authority}${uriPath}`;
} else if (
diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts
index f799065774..e1a0316d5f 100644
--- a/src/vs/base/parts/ipc/common/ipc.net.ts
+++ b/src/vs/base/parts/ipc/common/ipc.net.ts
@@ -151,7 +151,7 @@ export const enum ProtocolConstants {
/**
* If there is no reconnection within this time-frame, consider the connection permanently closed...
*/
- ReconnectionGraceTime = 60 * 60 * 1000, // 1hr
+ ReconnectionGraceTime = 3 * 60 * 60 * 1000, // 3hrs
}
class ProtocolMessage {
@@ -687,6 +687,10 @@ export class PersistentProtocol {
this._recvKeepAliveCheck();
}
+ public acceptDisconnect(): void {
+ this._onClose.fire();
+ }
+
private _receiveMessage(msg: ProtocolMessage): void {
if (msg.ack > this._outgoingAckId) {
this._outgoingAckId = msg.ack;
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index f2ae8517da..7c682fe74e 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -557,8 +557,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig);
// Delete some properties we do not want during reload
- delete configuration.filesToOpen;
- delete configuration.filesToCreate;
+ delete configuration.filesToOpenOrCreate;
delete configuration.filesToDiff;
delete configuration.filesToWait;
diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts
index dd75ecd411..9159e6e935 100644
--- a/src/vs/code/electron-main/windows.ts
+++ b/src/vs/code/electron-main/windows.ts
@@ -91,8 +91,7 @@ interface IPathParseOptions {
}
interface IFileInputs {
- filesToOpen: IPath[];
- filesToCreate: IPath[];
+ filesToOpenOrCreate: IPath[];
filesToDiff: IPath[];
filesToWait?: IPathsToWaitFor;
remoteAuthority?: string;
@@ -112,9 +111,6 @@ interface IPathToOpen extends IPath {
// the remote authority for the Code instance to open. Undefined if not remote.
remoteAuthority?: string;
- // indicator to create the file path in the Code instance
- createFilePath?: boolean;
-
// optional label for the recent history
label?: string;
}
@@ -397,13 +393,9 @@ export class WindowsManager implements IWindowsMainService {
workspacesToOpen.push(path);
} else if (path.fileUri) {
if (!fileInputs) {
- fileInputs = { filesToCreate: [], filesToOpen: [], filesToDiff: [], remoteAuthority: path.remoteAuthority };
- }
- if (!path.createFilePath) {
- fileInputs.filesToOpen.push(path);
- } else {
- fileInputs.filesToCreate.push(path);
+ fileInputs = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority };
}
+ fileInputs.filesToOpenOrCreate.push(path);
} else if (path.backupPath) {
emptyToRestore.push({ backupFolder: basename(path.backupPath), remoteAuthority: path.remoteAuthority });
} else {
@@ -413,15 +405,14 @@ export class WindowsManager implements IWindowsMainService {
// When run with --diff, take the files to open as files to diff
// if there are exactly two files provided.
- if (fileInputs && openConfig.diffMode && fileInputs.filesToOpen.length === 2) {
- fileInputs.filesToDiff = fileInputs.filesToOpen;
- fileInputs.filesToOpen = [];
- fileInputs.filesToCreate = []; // diff ignores other files that do not exist
+ if (fileInputs && openConfig.diffMode && fileInputs.filesToOpenOrCreate.length === 2) {
+ fileInputs.filesToDiff = fileInputs.filesToOpenOrCreate;
+ fileInputs.filesToOpenOrCreate = [];
}
// When run with --wait, make sure we keep the paths to wait for
if (fileInputs && openConfig.waitMarkerFileURI) {
- fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpen, ...fileInputs.filesToCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
+ fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
}
//
@@ -551,7 +542,7 @@ export class WindowsManager implements IWindowsMainService {
if (potentialWindowsCount === 0 && fileInputs) {
// Find suitable window or folder path to open files in
- const fileToCheck = fileInputs.filesToOpen[0] || fileInputs.filesToCreate[0] || fileInputs.filesToDiff[0];
+ const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0];
// only look at the windows with correct authority
const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs!.remoteAuthority);
@@ -746,10 +737,9 @@ export class WindowsManager implements IWindowsMainService {
private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow {
window.focus(); // make sure window has focus
- const params: { filesToOpen?: IPath[], filesToCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {};
+ const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {};
if (fileInputs) {
- params.filesToOpen = fileInputs.filesToOpen;
- params.filesToCreate = fileInputs.filesToCreate;
+ params.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate;
params.filesToDiff = fileInputs.filesToDiff;
params.filesToWait = fileInputs.filesToWait;
}
@@ -958,7 +948,7 @@ export class WindowsManager implements IWindowsMainService {
if (pathToOpen && pathToOpen.folderUri) {
windowsToOpen.push(pathToOpen);
}
- } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Windows that were Empty
+ } else if (restoreWindows !== 'folders' && openedWindow.backupPath && !openedWindow.remoteAuthority) { // Local windows that were empty. Empty windows with backups will always be restored in open()
windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority });
}
}
@@ -1065,47 +1055,57 @@ export class WindowsManager implements IWindowsMainService {
anyPath = parsedPath.path;
}
- // open remote if either specified in the cli even if it is a local file. TODO: Future idea: resolve in remote host context.
+ // open remote if either specified in the cli even if it is a local file. TODO@aeschli: Future idea: resolve in remote host context.
const remoteAuthority = options.remoteAuthority;
const candidate = normalize(anyPath);
try {
const candidateStat = fs.statSync(candidate);
- if (candidateStat) {
- if (candidateStat.isFile()) {
+ if (candidateStat.isFile()) {
- // Workspace (unless disabled via flag)
- if (!forceOpenWorkspaceAsFile) {
- const workspace = this.workspacesMainService.resolveLocalWorkspaceSync(URI.file(candidate));
- if (workspace) {
- return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority: workspace.remoteAuthority };
- }
+ // Workspace (unless disabled via flag)
+ if (!forceOpenWorkspaceAsFile) {
+ const workspace = this.workspacesMainService.resolveLocalWorkspaceSync(URI.file(candidate));
+ if (workspace) {
+ return {
+ workspace: { id: workspace.id, configPath: workspace.configPath },
+ remoteAuthority: workspace.remoteAuthority,
+ exists: true
+ };
}
-
- // File
- return {
- fileUri: URI.file(candidate),
- lineNumber,
- columnNumber,
- remoteAuthority
- };
}
- // Folder (we check for isDirectory() because e.g. paths like /dev/null
- // are neither file nor folder but some external tools might pass them
- // over to us)
- else if (candidateStat.isDirectory()) {
- return {
- folderUri: URI.file(candidate),
- remoteAuthority
- };
- }
+ // File
+ return {
+ fileUri: URI.file(candidate),
+ lineNumber,
+ columnNumber,
+ remoteAuthority,
+ exists: true
+ };
+ }
+
+ // Folder (we check for isDirectory() because e.g. paths like /dev/null
+ // are neither file nor folder but some external tools might pass them
+ // over to us)
+ else if (candidateStat.isDirectory()) {
+ return {
+ folderUri: URI.file(candidate),
+ remoteAuthority,
+ exists: true
+ };
}
} catch (error) {
const fileUri = URI.file(candidate);
this.historyMainService.removeFromRecentlyOpened([fileUri]); // since file does not seem to exist anymore, remove from recent
+
+ // assume this is a file that does not yet exist
if (options && options.ignoreFileNotFound) {
- return { fileUri, createFilePath: true, remoteAuthority }; // assume this is a file that does not yet exist
+ return {
+ fileUri,
+ remoteAuthority,
+ exists: false
+ };
}
}
@@ -1279,8 +1279,7 @@ export class WindowsManager implements IWindowsMainService {
const fileInputs = options.fileInputs;
if (fileInputs) {
- configuration.filesToOpen = fileInputs.filesToOpen;
- configuration.filesToCreate = fileInputs.filesToCreate;
+ configuration.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate;
configuration.filesToDiff = fileInputs.filesToDiff;
configuration.filesToWait = fileInputs.filesToWait;
}
diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts
index 0273098c28..4995e98882 100644
--- a/src/vs/platform/editor/common/editor.ts
+++ b/src/vs/platform/editor/common/editor.ts
@@ -49,6 +49,15 @@ export interface IBaseResourceInput {
* looking at the scheme of the resource(s).
*/
readonly forceFile?: boolean;
+
+ /**
+ * Hint to indicate that this input should be treated as a
+ * untitled file.
+ *
+ * Without this hint, the editor service will make a guess by
+ * looking at the scheme of the resource(s).
+ */
+ readonly forceUntitled?: boolean;
}
export interface IResourceInput extends IBaseResourceInput {
diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts
index 2e4491c599..051f19e146 100644
--- a/src/vs/platform/files/common/files.ts
+++ b/src/vs/platform/files/common/files.ts
@@ -288,7 +288,12 @@ export function markAsFileSystemProviderError(error: Error, code: FileSystemProv
return error;
}
-export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderErrorCode {
+export function toFileSystemProviderErrorCode(error: Error | undefined | null): FileSystemProviderErrorCode {
+
+ // Guard against abuse
+ if (!error) {
+ return FileSystemProviderErrorCode.Unknown;
+ }
// FileSystemProviderError comes with the code
if (error instanceof FileSystemProviderError) {
@@ -758,12 +763,12 @@ export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096;
*/
export const ETAG_DISABLED = '';
-export function etag(mtime: number, size: number): string;
-export function etag(mtime: number | undefined, size: number | undefined): string | undefined;
-export function etag(mtime: number | undefined, size: number | undefined): string | undefined {
- if (typeof size !== 'number' || typeof mtime !== 'number') {
+export function etag(stat: { mtime: number, size: number }): string;
+export function etag(stat: { mtime: number | undefined, size: number | undefined }): string | undefined;
+export function etag(stat: { mtime: number | undefined, size: number | undefined }): string | undefined {
+ if (typeof stat.size !== 'number' || typeof stat.mtime !== 'number') {
return undefined;
}
- return mtime.toString(29) + size.toString(31);
+ return stat.mtime.toString(29) + stat.size.toString(31);
}
diff --git a/src/vs/platform/update/node/update.config.contribution.ts b/src/vs/platform/update/node/update.config.contribution.ts
index 6905c4407e..9956898dbb 100644
--- a/src/vs/platform/update/node/update.config.contribution.ts
+++ b/src/vs/platform/update/node/update.config.contribution.ts
@@ -44,6 +44,7 @@ configurationRegistry.registerConfiguration({
'update.showReleaseNotes': {
type: 'boolean',
default: true,
+ scope: ConfigurationScope.APPLICATION,
description: localize('showReleaseNotes', "Show Release Notes after an update. The Release Notes are fetched from a Microsoft online service."),
tags: ['usesOnlineServices']
}
diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
index 328530a4e0..c5118746f0 100644
--- a/src/vs/platform/windows/common/windows.ts
+++ b/src/vs/platform/windows/common/windows.ts
@@ -358,7 +358,7 @@ export const enum ReadyState {
export interface IPath extends IPathData {
- // the file path to open within a Code instance
+ // the file path to open within the instance
fileUri?: URI;
}
@@ -374,7 +374,7 @@ export interface IPathsToWaitForData {
export interface IPathData {
- // the file path to open within a Code instance
+ // the file path to open within the instance
fileUri?: UriComponents;
// the line number in the file path to open
@@ -382,11 +382,15 @@ export interface IPathData {
// the column number in the file path to open
columnNumber?: number;
+
+ // a hint that the file exists. if true, the
+ // file exists, if false it does not. with
+ // undefined the state is unknown.
+ exists?: boolean;
}
export interface IOpenFileRequest {
- filesToOpen?: IPathData[];
- filesToCreate?: IPathData[];
+ filesToOpenOrCreate?: IPathData[];
filesToDiff?: IPathData[];
filesToWait?: IPathsToWaitForData;
termProgram?: string;
@@ -430,8 +434,7 @@ export interface IWindowConfiguration extends ParsedArgs {
perfWindowLoadTime?: number;
perfEntries: ExportData;
- filesToOpen?: IPath[];
- filesToCreate?: IPath[];
+ filesToOpenOrCreate?: IPath[];
filesToDiff?: IPath[];
filesToWait?: IPathsToWaitFor;
termProgram?: string;
diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts
index f160cfddb5..e7cc17dcf1 100644
--- a/src/vs/platform/windows/electron-browser/windowsService.ts
+++ b/src/vs/platform/windows/electron-browser/windowsService.ts
@@ -74,9 +74,11 @@ export class WindowsService implements IWindowsService {
return this.channel.call('closeWorkspace', windowId);
}
- enterWorkspace(windowId: number, path: URI): Promise {
+ enterWorkspace(windowId: number, path: URI): Promise {
return this.channel.call('enterWorkspace', [windowId, path]).then((result: IEnterWorkspaceResult) => {
- result.workspace = reviveWorkspaceIdentifier(result.workspace);
+ if (result) {
+ result.workspace = reviveWorkspaceIdentifier(result.workspace);
+ }
return result;
});
}
diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts
index 1d532f2a1e..b0140617f5 100644
--- a/src/vs/platform/windows/electron-main/windowsService.ts
+++ b/src/vs/platform/windows/electron-main/windowsService.ts
@@ -176,7 +176,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
async getRecentlyOpened(windowId: number): Promise {
this.logService.trace('windowsService#getRecentlyOpened', windowId);
- return this.withWindow(windowId, codeWindow => this.historyService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpen), () => this.historyService.getRecentlyOpened())!;
+ return this.withWindow(windowId, codeWindow => this.historyService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyService.getRecentlyOpened())!;
}
async newWindowTab(): Promise {
diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts
index 84ec1af6c2..a7898ab9f0 100644
--- a/src/vs/platform/workspaces/common/workspaces.ts
+++ b/src/vs/platform/workspaces/common/workspaces.ts
@@ -10,7 +10,7 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works
import { URI, UriComponents } from 'vs/base/common/uri';
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
import { extname } from 'vs/base/common/path';
-import { dirname, resolvePath, isEqualAuthority, isEqualOrParent, relativePath } from 'vs/base/common/resources';
+import { dirname, resolvePath, isEqualAuthority, isEqualOrParent, relativePath, extname as resourceExtname } from 'vs/base/common/resources';
import * as jsonEdit from 'vs/base/common/jsonEdit';
import * as json from 'vs/base/common/json';
import { Schemas } from 'vs/base/common/network';
@@ -158,8 +158,10 @@ export function isSingleFolderWorkspaceInitializationPayload(obj: any): obj is I
const WORKSPACE_SUFFIX = '.' + WORKSPACE_EXTENSION;
-export function hasWorkspaceFileExtension(path: string) {
- return extname(path) === WORKSPACE_SUFFIX;
+export function hasWorkspaceFileExtension(path: string | URI) {
+ const ext = (typeof path === 'string') ? extname(path) : resourceExtname(path);
+
+ return ext === WORKSPACE_SUFFIX;
}
const SLASH = '/';
diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts
index f0c6f54270..1e4af393ec 100644
--- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts
+++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts
@@ -61,7 +61,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
}
private isWorkspacePath(uri: URI): boolean {
- return this.isInsideWorkspacesHome(uri) || hasWorkspaceFileExtension(uri.path);
+ return this.isInsideWorkspacesHome(uri) || hasWorkspaceFileExtension(uri);
}
private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null {
diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts
index 3083ad95a3..af71f02cdb 100644
--- a/src/vs/workbench/api/common/apiCommands.ts
+++ b/src/vs/workbench/api/common/apiCommands.ts
@@ -36,6 +36,7 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
interface IOpenFolderAPICommandOptions {
forceNewWindow?: boolean;
+ forceReuseWindow?: boolean;
noRecentEntry?: boolean;
}
@@ -50,9 +51,9 @@ export class OpenFolderAPICommand {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow });
}
- const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
+ const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
- const uriToOpen: IURIToOpen = (hasWorkspaceFileExtension(uri.path) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
+ const uriToOpen: IURIToOpen = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
}
}
diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts
index d586c10196..f0e18376df 100644
--- a/src/vs/workbench/browser/dnd.ts
+++ b/src/vs/workbench/browser/dnd.ts
@@ -260,7 +260,7 @@ export class ResourcesDropHandler {
return Promise.all(fileOnDiskResources.map(fileOnDiskResource => {
// Check for Workspace
- if (hasWorkspaceFileExtension(fileOnDiskResource.fsPath)) {
+ if (hasWorkspaceFileExtension(fileOnDiskResource)) {
urisToOpen.push({ workspaceUri: fileOnDiskResource });
return undefined;
diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts
index f0de12726b..8c59677c0d 100644
--- a/src/vs/workbench/browser/layout.ts
+++ b/src/vs/workbench/browser/layout.ts
@@ -10,8 +10,7 @@ import { onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/brow
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { Registry } from 'vs/platform/registry/common/platform';
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
-import { IResourceInput } from 'vs/platform/editor/common/editor';
-import { IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor';
+import { IUntitledResourceInput, pathsToEditors } from 'vs/workbench/common/editor';
import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart';
import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
@@ -24,7 +23,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
-import { IWindowService, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
+import { IWindowService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -34,7 +33,7 @@ import { IDimension } from 'vs/platform/layout/browser/layoutService';
import { Part } from 'vs/workbench/browser/part';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
-import { coalesce } from 'vs/base/common/arrays';
+import { IFileService } from 'vs/platform/files/common/files';
enum Settings {
MENUBAR_VISIBLE = 'window.menuBarVisibility',
@@ -182,7 +181,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.registerLayoutListeners();
// State
- this.initLayoutState(accessor.get(ILifecycleService));
+ this.initLayoutState(accessor.get(ILifecycleService), accessor.get(IFileService));
}
private registerLayoutListeners(): void {
@@ -319,7 +318,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
}
- private initLayoutState(lifecycleService: ILifecycleService): void {
+ private initLayoutState(lifecycleService: ILifecycleService, fileService: IFileService): void {
// Fullscreen
this.state.fullscreen = isFullscreen();
@@ -358,7 +357,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false);
// Editors to open
- this.state.editor.editorsToOpen = this.resolveEditorsToOpen();
+ this.state.editor.editorsToOpen = this.resolveEditorsToOpen(fileService);
// Panel visibility
this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true);
@@ -389,7 +388,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE);
}
- private resolveEditorsToOpen(): Promise | IResourceEditor[] {
+ private resolveEditorsToOpen(fileService: IFileService): Promise | IResourceEditor[] {
const configuration = this.environmentService.configuration;
const hasInitialFilesToOpen = this.hasInitialFilesToOpen();
@@ -400,21 +399,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
if (hasInitialFilesToOpen) {
// Files to diff is exclusive
- const filesToDiff = this.toInputs(configuration.filesToDiff, false);
- if (filesToDiff && filesToDiff.length === 2) {
- return [{
- leftResource: filesToDiff[0].resource,
- rightResource: filesToDiff[1].resource,
- options: { pinned: true },
- forceFile: true
- }];
- }
+ return pathsToEditors(configuration.filesToDiff, fileService).then(filesToDiff => {
+ if (filesToDiff && filesToDiff.length === 2) {
+ return [{
+ leftResource: filesToDiff[0].resource,
+ rightResource: filesToDiff[1].resource,
+ options: { pinned: true },
+ forceFile: true
+ }];
+ }
- const filesToCreate = this.toInputs(configuration.filesToCreate, true);
- const filesToOpen = this.toInputs(configuration.filesToOpen, false);
-
- // Otherwise: Open/Create files
- return [...filesToOpen, ...filesToCreate];
+ // Otherwise: Open/Create files
+ return pathsToEditors(configuration.filesToOpenOrCreate, fileService);
+ });
}
// Empty workbench
@@ -439,38 +436,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
const configuration = this.environmentService.configuration;
return !!(
- (configuration.filesToCreate && configuration.filesToCreate.length > 0) ||
- (configuration.filesToOpen && configuration.filesToOpen.length > 0) ||
- (configuration.filesToDiff && configuration.filesToDiff.length > 0));
- }
-
- private toInputs(paths: IPath[] | undefined, isNew: boolean): Array {
- if (!paths || !paths.length) {
- return [];
- }
-
- return coalesce(paths.map(p => {
- const resource = p.fileUri;
- if (!resource) {
- return undefined;
- }
-
- let input: IResourceInput | IUntitledResourceInput;
- if (isNew) {
- input = { filePath: resource.fsPath, options: { pinned: true } };
- } else {
- input = { resource, options: { pinned: true }, forceFile: true };
- }
-
- if (!isNew && typeof p.lineNumber === 'number') {
- input.options!.selection = {
- startLineNumber: p.lineNumber,
- startColumn: p.columnNumber || 1
- };
- }
-
- return input;
- }));
+ (configuration.filesToOpenOrCreate && configuration.filesToOpenOrCreate.length > 0) ||
+ (configuration.filesToDiff && configuration.filesToDiff.length > 0)
+ );
}
private updatePanelPosition() {
diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts
index a661f4e2ea..6db31aa25c 100644
--- a/src/vs/workbench/browser/nodeless.simpleservices.ts
+++ b/src/vs/workbench/browser/nodeless.simpleservices.ts
@@ -953,8 +953,7 @@ export class SimpleWindowConfiguration implements IWindowConfiguration {
perfWindowLoadTime?: number;
perfEntries: ExportData;
- filesToOpen?: IPath[];
- filesToCreate?: IPath[];
+ filesToOpenOrCreate?: IPath[];
filesToDiff?: IPath[];
filesToWait?: IPathsToWaitFor;
termProgram?: string;
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
index ee97ec5168..5f3b186cb4 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
@@ -76,7 +76,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
@IStorageService private readonly storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@IViewsService private readonly viewsService: IViewsService,
- @IContextKeyService private readonly contextKeyService: IContextKeyService,
+ @IContextKeyService private readonly contextKeyService: IContextKeyService
) {
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
@@ -150,7 +150,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (viewContainer && viewContainer.hideIfEmpty) {
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
- this.removeComposite(viewletDescriptor.id, true); // Update the composite bar by hiding
+ this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding
}
}
}
@@ -309,14 +309,14 @@ export class ActivitybarPart extends Part implements IActivityBarService {
disposable.dispose();
}
this.viewletDisposables.delete(viewletId);
- this.removeComposite(viewletId, true);
+ this.hideComposite(viewletId);
}
private onDidChangeActiveViews(viewlet: ViewletDescriptor, viewDescriptors: IViewDescriptorCollection): void {
if (viewDescriptors.activeViewDescriptors.length) {
this.compositeBar.addComposite(viewlet);
} else {
- this.removeComposite(viewlet.id, true);
+ this.hideComposite(viewlet.id);
}
}
@@ -334,18 +334,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
const viewlets = this.viewletService.getViewlets();
for (const { id } of this.cachedViewlets) {
if (viewlets.every(viewlet => viewlet.id !== id)) {
- this.removeComposite(id, false);
+ this.hideComposite(id);
}
}
}
- private removeComposite(compositeId: string, hide: boolean): void {
- if (hide) {
- this.compositeBar.hideComposite(compositeId);
- } else {
- this.compositeBar.removeComposite(compositeId);
- }
-
+ private hideComposite(compositeId: string): void {
+ this.compositeBar.hideComposite(compositeId);
const compositeActions = this.compositeActions[compositeId];
if (compositeActions) {
compositeActions.activityAction.dispose();
@@ -441,7 +436,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
}
}
- state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl && viewlet.iconUrl.scheme === Schemas.file ? viewlet.iconUrl : undefined, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible });
+ state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl && viewlet.iconUrl.scheme === Schemas.file ? viewlet.iconUrl : undefined, views, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible });
+ } else {
+ state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: false });
}
}
diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts
index 115f1f45b3..fb7703439e 100644
--- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts
+++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts
@@ -19,6 +19,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { CancellationToken } from 'vs/base/common/cancellation';
import { dispose } from 'vs/base/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => Promise;
@@ -48,6 +49,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
telemetryService: ITelemetryService,
themeService: IThemeService,
@ITextFileService private readonly textFileService: ITextFileService,
+ @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageService storageService: IStorageService
) {
super(id, telemetryService, themeService, storageService);
@@ -92,9 +94,11 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
this.textFileService,
this.binaryContainer,
this.scrollbar,
- resource => this.handleOpenInternalCallback(input, options),
- resource => this.callbacks.openExternal(resource),
- meta => this.handleMetadataChanged(meta)
+ {
+ openInternalClb: _ => this.handleOpenInternalCallback(input, options),
+ openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource),
+ metadataClb: meta => this.handleMetadataChanged(meta)
+ }
);
return undefined;
diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts
index c25e3c8e08..e9ecda5eae 100644
--- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts
+++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts
@@ -47,6 +47,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
+import { ILabelService } from 'vs/platform/label/common/label';
class Item extends BreadcrumbsItem {
@@ -167,6 +168,7 @@ export class BreadcrumbsControl {
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IFileService private readonly _fileService: IFileService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
+ @ILabelService private readonly _labelService: ILabelService,
@IBreadcrumbsService breadcrumbsService: IBreadcrumbsService,
) {
this.domNode = document.createElement('div');
@@ -238,16 +240,18 @@ export class BreadcrumbsControl {
this._ckBreadcrumbsVisible.set(true);
this._ckBreadcrumbsPossible.set(true);
- let editor = this._getActiveCodeEditor();
- let model = new EditorBreadcrumbsModel(input.getResource()!, editor, this._workspaceService, this._configurationService);
+ const uri = input.getResource()!;
+ const editor = this._getActiveCodeEditor();
+ const model = new EditorBreadcrumbsModel(uri, editor, this._workspaceService, this._configurationService);
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
+ dom.toggleClass(this.domNode, 'backslash-path', this._labelService.getSeparator(uri.scheme, uri.authority) === '\\');
- let updateBreadcrumbs = () => {
- let items = model.getElements().map(element => new Item(element, this._options, this._instantiationService));
+ const updateBreadcrumbs = () => {
+ const items = model.getElements().map(element => new Item(element, this._options, this._instantiationService));
this._widget.setItems(items);
this._widget.reveal(items[items.length - 1]);
};
- let listener = model.onDidUpdate(updateBreadcrumbs);
+ const listener = model.onDidUpdate(updateBreadcrumbs);
updateBreadcrumbs();
this._breadcrumbsDisposables = [model, listener];
diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts
index 884cd78691..ca335f65e1 100644
--- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts
+++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts
@@ -47,7 +47,6 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { isMacintosh } from 'vs/base/common/platform';
import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
-import { Schemas } from 'vs/base/common/network';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { OpenWorkspaceButtonContribution } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { ZoomStatusbarItem } from 'vs/workbench/browser/parts/editor/resourceViewer';
@@ -148,11 +147,10 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
return instantiationService.invokeFunction(accessor => {
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
- const filePath = resource.scheme === Schemas.untitled ? undefined : resource.scheme === Schemas.file ? resource.fsPath : resource.path;
const language = deserialized.modeId;
const encoding = deserialized.encoding;
- return accessor.get(IEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
+ return accessor.get(IEditorService).createInput({ resource, language, encoding, forceUntitled: true }) as UntitledEditorInput;
});
}
}
diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts
index 68e29d985e..9fa7efad88 100644
--- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts
+++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts
@@ -51,7 +51,8 @@ import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorServic
import { withNullAsUndefined } from 'vs/base/common/types';
import { hash } from 'vs/base/common/hash';
import { guessMimeTypes } from 'vs/base/common/mime';
-import { extname } from 'vs/base/common/path';
+import { extname } from 'vs/base/common/resources';
+import { Schemas } from 'vs/base/common/network';
export class EditorGroupView extends Themable implements IEditorGroupView {
@@ -526,8 +527,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
const descriptor = editor.getTelemetryDescriptor();
const resource = editor.getResource();
- if (resource && resource.fsPath) {
- descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hash(resource.fsPath) };
+ const path = resource ? resource.scheme === Schemas.file ? resource.fsPath : resource.path : undefined;
+ if (resource && path) {
+ descriptor['resource'] = { mimeType: guessMimeTypes(path).join(', '), scheme: resource.scheme, ext: extname(resource), path: hash(path) };
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts
index 17825d8555..9238d3a726 100644
--- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts
+++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts
@@ -139,7 +139,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
return false; // we need a model
}
- if (!hasWorkspaceFileExtension(model.uri.fsPath)) {
+ if (!hasWorkspaceFileExtension(model.uri)) {
return false; // we need a workspace file
}
diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css
index 34176cb720..d505c304cd 100644
--- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css
+++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css
@@ -54,7 +54,7 @@
background-image: none;
}
-.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
+.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.backslash-path .monaco-breadcrumb-item::before {
content: '\\';
}
diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts
index 7a71f93428..a022d952e2 100644
--- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts
+++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts
@@ -62,6 +62,12 @@ export interface ResourceViewerContext extends IDisposable {
layout?(dimension: DOM.Dimension): void;
}
+interface ResourceViewerDelegate {
+ openInternalClb(uri: URI): void;
+ openExternalClb?(uri: URI): void;
+ metadataClb(meta: string): void;
+}
+
/**
* Helper to actually render the given resource into the provided container. Will adjust scrollbar (if provided) automatically based on loading
* progress of the binary resource.
@@ -75,9 +81,7 @@ export class ResourceViewer {
textFileService: ITextFileService,
container: HTMLElement,
scrollbar: DomScrollableElement,
- openInternalClb: (uri: URI) => void,
- openExternalClb: (uri: URI) => void,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
): ResourceViewerContext {
// Ensure CSS class
@@ -85,17 +89,17 @@ export class ResourceViewer {
// Images
if (ResourceViewer.isImageResource(descriptor)) {
- return ImageView.create(container, descriptor, textFileService, scrollbar, openExternalClb, metadataClb);
+ return ImageView.create(container, descriptor, textFileService, scrollbar, delegate);
}
// Large Files
if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
- return FileTooLargeFileView.create(container, descriptor, scrollbar, metadataClb);
+ return FileTooLargeFileView.create(container, descriptor, scrollbar, delegate);
}
// Seemingly Binary Files
else {
- return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, openInternalClb, metadataClb);
+ return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, delegate);
}
}
@@ -116,14 +120,13 @@ class ImageView {
descriptor: IResourceDescriptor,
textFileService: ITextFileService,
scrollbar: DomScrollableElement,
- openExternalClb: (uri: URI) => void,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
): ResourceViewerContext {
if (ImageView.shouldShowImageInline(descriptor)) {
- return InlineImageView.create(container, descriptor, textFileService, scrollbar, metadataClb);
+ return InlineImageView.create(container, descriptor, textFileService, scrollbar, delegate);
}
- return LargeImageView.create(container, descriptor, openExternalClb, metadataClb);
+ return LargeImageView.create(container, descriptor, delegate);
}
private static shouldShowImageInline(descriptor: IResourceDescriptor): boolean {
@@ -150,11 +153,10 @@ class LargeImageView {
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
- openExternalClb: (uri: URI) => void,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
) {
const size = BinarySize.formatSize(descriptor.size);
- metadataClb(size);
+ delegate.metadataClb(size);
DOM.clearNode(container);
@@ -164,12 +166,13 @@ class LargeImageView {
label.textContent = nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size);
container.appendChild(label);
- if (descriptor.resource.scheme === Schemas.file) {
+ const openExternal = delegate.openExternalClb;
+ if (descriptor.resource.scheme === Schemas.file && openExternal) {
const link = DOM.append(label, DOM.$('a.embedded-link'));
link.setAttribute('role', 'button');
link.textContent = nls.localize('resourceOpenExternalButton', "Open image using external program?");
- disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openExternalClb(descriptor.resource)));
+ disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openExternal(descriptor.resource)));
}
return combinedDisposable(disposables);
@@ -181,10 +184,10 @@ class FileTooLargeFileView {
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
) {
const size = BinarySize.formatSize(descriptor.size);
- metadataClb(size);
+ delegate.metadataClb(size);
DOM.clearNode(container);
@@ -203,10 +206,9 @@ class FileSeemsBinaryFileView {
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
- openInternalClb: (uri: URI) => void,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
) {
- metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
+ delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
DOM.clearNode(container);
@@ -221,7 +223,7 @@ class FileSeemsBinaryFileView {
link.setAttribute('role', 'button');
link.textContent = nls.localize('openAsText', "Do you want to open it anyway?");
- disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openInternalClb(descriptor.resource)));
+ disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource)));
}
scrollbar.scanDomNode();
@@ -359,7 +361,7 @@ class InlineImageView {
descriptor: IResourceDescriptor,
textFileService: ITextFileService,
scrollbar: DomScrollableElement,
- metadataClb: (meta: string) => void
+ delegate: ResourceViewerDelegate
) {
const disposables: IDisposable[] = [];
@@ -543,9 +545,9 @@ class InlineImageView {
return;
}
if (typeof descriptor.size === 'number') {
- metadataClb(nls.localize('imgMeta', '{0}x{1} {2}', image.naturalWidth, image.naturalHeight, BinarySize.formatSize(descriptor.size)));
+ delegate.metadataClb(nls.localize('imgMeta', '{0}x{1} {2}', image.naturalWidth, image.naturalHeight, BinarySize.formatSize(descriptor.size)));
} else {
- metadataClb(nls.localize('imgMetaNoSize', '{0}x{1}', image.naturalWidth, image.naturalHeight));
+ delegate.metadataClb(nls.localize('imgMetaNoSize', '{0}x{1}', image.naturalWidth, image.naturalHeight));
}
scrollbar.scanDomNode();
diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts
index 5e53918fc9..bf7077e124 100644
--- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts
+++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts
@@ -255,7 +255,7 @@ class QuickInput implements IQuickInput {
this.ui.leftActionBar.clear();
const leftButtons = this.buttons.filter(button => button === backButton);
this.ui.leftActionBar.push(leftButtons.map((button, index) => {
- const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
+ const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
this.onDidTriggerButtonEmitter.fire(button);
return Promise.resolve(null);
});
@@ -265,7 +265,7 @@ class QuickInput implements IQuickInput {
this.ui.rightActionBar.clear();
const rightButtons = this.buttons.filter(button => button !== backButton);
this.ui.rightActionBar.push(rightButtons.map((button, index) => {
- const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
+ const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
this.onDidTriggerButtonEmitter.fire(button);
return Promise.resolve(null);
});
@@ -757,7 +757,7 @@ class QuickPick extends QuickInput implements IQuickPi
this.showMessageDecoration(Severity.Error);
} else {
this.ui.message.textContent = null;
- this.showMessageDecoration(Severity.Info);
+ this.showMessageDecoration(Severity.Ignore);
}
this.ui.customButton.label = this.customLabel;
this.ui.customButton.element.title = this.customHover;
@@ -888,7 +888,7 @@ class InputBox extends QuickInput implements IInputBox {
}
if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) {
this.ui.message.textContent = this.noValidationMessage;
- this.showMessageDecoration(Severity.Info);
+ this.showMessageDecoration(Severity.Ignore);
}
if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) {
this.ui.message.textContent = this.validationMessage;
@@ -1571,4 +1571,4 @@ export class BackAction extends Action {
}
}
-registerSingleton(IQuickInputService, QuickInputService, true);
\ No newline at end of file
+registerSingleton(IQuickInputService, QuickInputService, true);
diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts
index 34093d1c88..371651f119 100644
--- a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts
+++ b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts
@@ -11,7 +11,10 @@ import { IdGenerator } from 'vs/base/common/idGenerator';
const iconPathToClass = {};
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
-export function getIconClass(iconPath: { dark: URI; light?: URI; }) {
+export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined): string | undefined {
+ if (!iconPath) {
+ return undefined;
+ }
let iconClass: string;
const key = iconPath.dark.toString();
diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
index f869dcce7d..8e8d7805cd 100644
--- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
+++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
@@ -36,6 +36,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { Schemas } from 'vs/base/common/network';
export class TitlebarPart extends Part implements ITitleService {
@@ -179,7 +180,7 @@ export class TitlebarPart extends Part implements ITitleService {
}
private updateRepresentedFilename(): void {
- const file = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: 'file' });
+ const file = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.file });
const path = file ? file.fsPath : '';
// Apply to window
diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts
index 419c4fa1ba..abb91f3dda 100644
--- a/src/vs/workbench/common/editor.ts
+++ b/src/vs/workbench/common/editor.ts
@@ -9,7 +9,7 @@ import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
-import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput } from 'vs/platform/editor/common/editor';
+import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput } from 'vs/platform/editor/common/editor';
import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -17,6 +17,9 @@ import { ITextModel } from 'vs/editor/common/model';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ICompositeControl } from 'vs/workbench/common/composite';
import { ActionRunner, IAction } from 'vs/base/common/actions';
+import { IFileService } from 'vs/platform/files/common/files';
+import { IPathData } from 'vs/platform/windows/common/windows';
+import { coalesce } from 'vs/base/common/arrays';
export const ActiveEditorContext = new RawContextKey('activeEditor', null);
export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false);
@@ -198,15 +201,11 @@ export interface IEditorInputFactory {
export interface IUntitledResourceInput extends IBaseResourceInput {
/**
- * Optional resource. If the resource is not provided a new untitled file is created.
+ * Optional resource. If the resource is not provided a new untitled file is created (e.g. Untitled-1).
+ * Otherwise the untitled editor will have an associated path and use that when saving.
*/
resource?: URI;
- /**
- * Optional file path. Using the file resource will associate the file to the untitled resource.
- */
- filePath?: string;
-
/**
* Optional language of the untitled resource.
*/
@@ -1089,3 +1088,37 @@ export const Extensions = {
};
Registry.add(Extensions.EditorInputFactories, new EditorInputFactoryRegistry());
+
+export async function pathsToEditors(paths: IPathData[] | undefined, fileService: IFileService): Promise<(IResourceInput | IUntitledResourceInput)[]> {
+ if (!paths || !paths.length) {
+ return [];
+ }
+
+ const editors = await Promise.all(paths.map(async path => {
+ const resource = URI.revive(path.fileUri);
+ if (!resource || !fileService.canHandleResource(resource)) {
+ return undefined; // {{SQL CARBON EDIT}} @anthonydresser revert after strictnullchecks
+ }
+
+ const exists = (typeof path.exists === 'boolean') ? path.exists : await fileService.exists(resource);
+
+ const options: ITextEditorOptions = { pinned: true };
+ if (exists && typeof path.lineNumber === 'number') {
+ options.selection = {
+ startLineNumber: path.lineNumber,
+ startColumn: path.columnNumber || 1
+ };
+ }
+
+ let input: IResourceInput | IUntitledResourceInput;
+ if (!exists) {
+ input = { resource, options, forceUntitled: true };
+ } else {
+ input = { resource, options, forceFile: true };
+ }
+
+ return input;
+ }));
+
+ return coalesce(editors);
+}
diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts
index 1569b5dc42..531fe4d7e1 100644
--- a/src/vs/workbench/common/theme.ts
+++ b/src/vs/workbench/common/theme.ts
@@ -310,15 +310,15 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB
}, nls.localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window."));
export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.hostBackground', {
- dark: STATUS_BAR_PROMINENT_ITEM_BACKGROUND,
- light: STATUS_BAR_PROMINENT_ITEM_BACKGROUND,
- hc: STATUS_BAR_PROMINENT_ITEM_BACKGROUND
+ dark: '#C40057',
+ light: '#C40057',
+ hc: '#C40057'
}, nls.localize('statusBarItemHostBackground', "Background color for the host indicator on the status bar."));
export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.hostForeground', {
- dark: STATUS_BAR_PROMINENT_ITEM_FOREGROUND,
- light: STATUS_BAR_PROMINENT_ITEM_FOREGROUND,
- hc: STATUS_BAR_PROMINENT_ITEM_FOREGROUND
+ dark: '#FFFFFF',
+ light: '#FFFFFF',
+ hc: '#FFFFFF'
}, nls.localize('statusBarItemHostForeground', "Foreground color for the host indicator on the status bar."));
diff --git a/src/vs/workbench/contrib/backup/common/backupRestorer.ts b/src/vs/workbench/contrib/backup/common/backupRestorer.ts
index 007d12c506..99662104d3 100644
--- a/src/vs/workbench/contrib/backup/common/backupRestorer.ts
+++ b/src/vs/workbench/contrib/backup/common/backupRestorer.ts
@@ -11,6 +11,8 @@ import { IResourceInput } from 'vs/platform/editor/common/editor';
import { Schemas } from 'vs/base/common/network';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IUntitledResourceInput } from 'vs/workbench/common/editor';
+import { toLocalResource } from 'vs/base/common/resources';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export class BackupRestorer implements IWorkbenchContribution {
@@ -21,7 +23,8 @@ export class BackupRestorer implements IWorkbenchContribution {
constructor(
@IEditorService private readonly editorService: IEditorService,
@IBackupFileService private readonly backupFileService: IBackupFileService,
- @ILifecycleService private readonly lifecycleService: ILifecycleService
+ @ILifecycleService private readonly lifecycleService: ILifecycleService,
+ @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
this.restoreBackups();
}
@@ -79,9 +82,9 @@ export class BackupRestorer implements IWorkbenchContribution {
if (resource.scheme === Schemas.untitled
&& !BackupRestorer.UNTITLED_REGEX.test(resource.fsPath)
&& !BackupRestorer.SQLQUERY_REGEX.test(resource.fsPath)) {
- return { filePath: resource.fsPath, options };
+ return { resource: toLocalResource(resource, this.environmentService.configuration.remoteAuthority), options, forceUntitled: true };
}
return { resource, options };
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts
index 77050d40e8..06e89f078e 100644
--- a/src/vs/workbench/contrib/extensions/common/extensions.ts
+++ b/src/vs/workbench/contrib/extensions/common/extensions.ts
@@ -85,6 +85,7 @@ export interface IExtensionsWorkbenchService {
_serviceBrand: any;
onChange: Event;
local: IExtension[];
+ installed: IExtension[];
outdated: IExtension[];
queryLocal(server?: IExtensionManagementServer): Promise;
queryGallery(token: CancellationToken): Promise>;
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts
index 7f6f31331a..aafff0590f 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionEditor.ts
@@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
-import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, DisabledLabelAction, SystemDisabledWarningAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
+import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, DisabledLabelAction, SystemDisabledWarningAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@@ -300,7 +300,7 @@ export class ExtensionEditor extends BaseEditor {
this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token)));
this.extensionDependencies = new Cache(() => createCancelablePromise(token => this.extensionsWorkbenchService.loadDependencies(extension, token)));
- const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer);
+ const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer, true);
const onError = Event.once(domEvent(this.icon, 'error'));
onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables);
this.icon.src = extension.iconUrl;
@@ -393,6 +393,7 @@ export class ExtensionEditor extends BaseEditor {
this.instantiationService.createInstance(EnableDropDownAction),
this.instantiationService.createInstance(DisableDropDownAction, runningExtensions),
this.instantiationService.createInstance(RemoteInstallAction),
+ this.instantiationService.createInstance(LocalInstallAction),
combinedInstallAction,
systemDisabledWarningAction,
this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction),
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts
index 3da3e630b4..3a774fd4bb 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts
@@ -316,12 +316,14 @@ export class RemoteInstallAction extends ExtensionAction {
private updateLabel(): void {
if (this.installing) {
this.label = RemoteInstallAction.INSTALLING_LABEL;
+ this.tooltip = this.label;
return;
}
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
if (remoteAuthority) {
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.label = `${RemoteInstallAction.INSTALL_LABEL} on ${host}`;
+ this.tooltip = this.label;
return;
}
}
@@ -339,9 +341,9 @@ export class RemoteInstallAction extends ExtensionAction {
// Installed User Extension
&& this.extension && this.extension.local && this.extension.type === ExtensionType.User && this.extension.state === ExtensionState.Installed
// Local Workspace Extension
- && this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
+ && this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && (isLanguagePackExtension(this.extension.local.manifest) || !isUIExtension(this.extension.local.manifest, this.configurationService))
// Extension does not exist in remote
- && !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
+ && !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
&& this.extensionsWorkbenchService.canInstall(this.extension)
) {
this.enabled = true;
@@ -370,6 +372,85 @@ export class RemoteInstallAction extends ExtensionAction {
}
}
+export class LocalInstallAction extends ExtensionAction {
+
+ private static INSTALL_LABEL = localize('install locally', "Install Locally");
+ private static INSTALLING_LABEL = localize('installing', "Installing");
+
+ private static readonly Class = 'extension-action prominent install';
+ private static readonly InstallingClass = 'extension-action install installing';
+
+ updateWhenCounterExtensionChanges: boolean = true;
+ private disposables: IDisposable[] = [];
+ private installing: boolean = false;
+
+ constructor(
+ @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
+ @ILabelService private readonly labelService: ILabelService,
+ @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
+ @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
+ @IConfigurationService private readonly configurationService: IConfigurationService,
+ ) {
+ super(`extensions.localinstall`, LocalInstallAction.INSTALL_LABEL, LocalInstallAction.Class, false);
+ this.labelService.onDidChangeFormatters(() => this.updateLabel(), this, this.disposables);
+ this.updateLabel();
+ this.update();
+ }
+
+ private updateLabel(): void {
+ if (this.installing) {
+ this.label = LocalInstallAction.INSTALLING_LABEL;
+ this.tooltip = this.label;
+ return;
+ }
+ this.label = `${LocalInstallAction.INSTALL_LABEL}`;
+ this.tooltip = this.label;
+ }
+
+ update(): void {
+ this.enabled = false;
+ this.class = LocalInstallAction.Class;
+ if (this.installing) {
+ this.enabled = true;
+ this.class = LocalInstallAction.InstallingClass;
+ this.updateLabel();
+ return;
+ }
+ if (this.environmentService.configuration.remoteAuthority
+ // Installed User Extension
+ && this.extension && this.extension.local && this.extension.type === ExtensionType.User && this.extension.state === ExtensionState.Installed
+ // Remote UI or Language pack Extension
+ && this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && (isLanguagePackExtension(this.extension.local.manifest) || isUIExtension(this.extension.local.manifest, this.configurationService))
+ // Extension does not exist in local
+ && !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.localExtensionManagementServer)
+ && this.extensionsWorkbenchService.canInstall(this.extension)
+ ) {
+ this.enabled = true;
+ this.updateLabel();
+ return;
+ }
+ }
+
+ async run(): Promise {
+ if (!this.installing) {
+ this.installing = true;
+ this.update();
+ this.extensionsWorkbenchService.open(this.extension);
+ alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
+ if (this.extension.gallery) {
+ await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(this.extension.gallery);
+ this.installing = false;
+ this.update();
+ }
+ }
+ }
+
+ dispose(): void {
+ this.disposables = dispose(this.disposables);
+ super.dispose();
+ }
+}
+
export class UninstallAction extends ExtensionAction {
private static readonly UninstallLabel = localize('uninstallAction', "Uninstall");
@@ -1265,23 +1346,34 @@ export class ReloadAction extends ExtensionAction {
if (isEnabled && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
- // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
- this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
+ this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension."); // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
return;
}
- if (this.workbenchEnvironmentService.configuration.remoteAuthority
+ if (this.workbenchEnvironmentService.configuration.remoteAuthority) {
+ const uiExtension = isUIExtension(this.extension.local.manifest, this.configurationService);
// Local Workspace Extension
- && this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
- ) {
- const remoteExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)[0];
- // Extension exist in remote and enabled
- if (remoteExtension && remoteExtension.local && this.extensionEnablementService.isEnabled(remoteExtension.local)) {
- this.enabled = true;
- this.label = localize('reloadRequired', "Reload Required");
- // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
- this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
- alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName));
- return;
+ if (!uiExtension && this.extension.server === this.extensionManagementServerService.localExtensionManagementServer) {
+ const remoteExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)[0];
+ // Extension exist in remote and enabled
+ if (remoteExtension && remoteExtension.local && this.extensionEnablementService.isEnabled(remoteExtension.local)) {
+ this.enabled = true;
+ this.label = localize('reloadRequired', "Reload Required");
+ this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
+ alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName)); // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
+ return;
+ }
+ }
+ // Remote UI Extension
+ if (uiExtension && this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
+ const localExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.localExtensionManagementServer)[0];
+ // Extension exist in local and enabled
+ if (localExtension && localExtension.local && this.extensionEnablementService.isEnabled(localExtension.local)) {
+ this.enabled = true;
+ this.label = localize('reloadRequired', "Reload Required");
+ this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension."); // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
+ alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Azure Data Studio to enable it.", this.extension.displayName)); // {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
+ return;
+ }
}
}
}
@@ -2464,7 +2556,8 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
}
constructor(
- @IExtensionService private readonly extensionService: IExtensionService
+ @IExtensionService private readonly extensionService: IExtensionService,
+ @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
) {
super('extensions.action.statusLabel', '', StatusLabelAction.DISABLED_CLASS, false);
}
@@ -2503,7 +2596,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer {
};
const canRemoveExtension = () => {
if (this.extension.local) {
- if (runningExtensions.every(e => !areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) {
+ if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value }, this.extension.identifier) && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation)))) {
return true;
}
return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local));
@@ -2592,21 +2685,18 @@ export class DisabledLabelAction extends ExtensionAction {
update(): void {
this.class = `${DisabledLabelAction.Class} hide`;
this.label = '';
- this.enabled = false;
- if (this.extension && this.extension.local && isLanguagePackExtension(this.extension.local.manifest)) {
- return;
- }
- if (this.warningAction.enabled) {
- this.enabled = true;
+ if (this.warningAction.tooltip) {
this.class = DisabledLabelAction.Class;
this.label = this.warningAction.tooltip;
return;
}
+ if (this.extension && this.extension.local && isLanguagePackExtension(this.extension.local.manifest)) {
+ return;
+ }
if (this.extension && this.extension.local && this._runningExtensions) {
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
const isExtensionRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier));
- if (!isExtensionRunning && !isEnabled) {
- this.enabled = true;
+ if (!isExtensionRunning && !isEnabled && this.extensionEnablementService.canChangeEnablement(this.extension.local)) {
this.class = DisabledLabelAction.Class;
this.label = localize('disabled by user', "This extension is disabled by the user.");
return;
@@ -2626,7 +2716,9 @@ export class DisabledLabelAction extends ExtensionAction {
export class SystemDisabledWarningAction extends ExtensionAction {
- private static readonly Class = 'disable-warning';
+ private static readonly CLASS = 'system-disable';
+ private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} warning`;
+ private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} info`;
updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = [];
@@ -2640,7 +2732,7 @@ export class SystemDisabledWarningAction extends ExtensionAction {
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly extensionService: IExtensionService,
) {
- super('extensions.install', '', `${SystemDisabledWarningAction.Class} hide`, false);
+ super('extensions.install', '', `${SystemDisabledWarningAction.CLASS} hide`, false);
this.labelService.onDidChangeFormatters(() => this.update(), this, this.disposables);
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
this.updateRunningExtensions();
@@ -2652,44 +2744,54 @@ export class SystemDisabledWarningAction extends ExtensionAction {
}
update(): void {
- this.enabled = false;
- this.class = `${SystemDisabledWarningAction.Class} hide`;
+ this.class = `${SystemDisabledWarningAction.CLASS} hide`;
this.tooltip = '';
- if (this.extension && this.extension.local && isLanguagePackExtension(this.extension.local.manifest)) {
+ if (
+ !this.extension ||
+ !this.extension.local ||
+ !this.extension.server ||
+ !this._runningExtensions ||
+ !this.workbenchEnvironmentService.configuration.remoteAuthority ||
+ !this.extensionManagementServerService.remoteExtensionManagementServer ||
+ this.extension.state !== ExtensionState.Installed
+ ) {
return;
}
- if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
- const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
- const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
- const localExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
- const localExtensionServer = localExtension ? localExtension.server : null;
- if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)) {
- if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
- this.enabled = true;
- this.class = `${SystemDisabledWarningAction.Class}`;
- this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
- return;
- }
- if (localExtensionServer !== this.extensionManagementServerService.remoteExtensionManagementServer) {
- this.enabled = true;
- this.class = `${SystemDisabledWarningAction.Class}`;
- this.tooltip = localize('Install in remote server', "Install the extension on '{0}' to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
- return;
- }
+ if (isLanguagePackExtension(this.extension.local.manifest)) {
+ if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server !== this.extension.server)) {
+ this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
+ this.tooltip = this.extension.server === this.extensionManagementServerService.localExtensionManagementServer
+ ? localize('Install language pack also in remote server', "Install the language pack extension on '{0}' to enable it also there.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer))
+ : localize('Install language pack also locally', "Install the language pack extension locally to enable it also there.");
}
- if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && isUIExtension(this.extension.local.manifest, this.configurationService)) {
- if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
- this.enabled = true;
- this.class = `${SystemDisabledWarningAction.Class}`;
- this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
- return;
- }
- if (localExtensionServer !== this.extensionManagementServerService.localExtensionManagementServer) {
- this.enabled = true;
- this.class = `${SystemDisabledWarningAction.Class}`;
- this.tooltip = localize('Install in local server', "Install the extension locally to enable.");
- return;
- }
+ return;
+ }
+ const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
+ const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
+ const localExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
+ const localExtensionServer = localExtension ? localExtension.server : null;
+ if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)) {
+ if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
+ this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
+ this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
+ return;
+ }
+ if (localExtensionServer !== this.extensionManagementServerService.remoteExtensionManagementServer) {
+ this.class = `${SystemDisabledWarningAction.WARNING_CLASS}`;
+ this.tooltip = localize('Install in remote server', "Install the extension on '{0}' to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
+ return;
+ }
+ }
+ if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && isUIExtension(this.extension.local.manifest, this.configurationService)) {
+ if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
+ this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
+ this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
+ return;
+ }
+ if (localExtensionServer !== this.extensionManagementServerService.localExtensionManagementServer) {
+ this.class = `${SystemDisabledWarningAction.WARNING_CLASS}`;
+ this.tooltip = localize('Install in local server', "Install the extension locally to enable.");
+ return;
}
}
}
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts
index fd4e30b2f5..7c0e7547bc 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsList.ts
@@ -13,7 +13,7 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { Event } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
-import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
+import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction, RemoteInstallAction, SystemDisabledWarningAction, DisabledLabelAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, TooltipWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -67,7 +67,7 @@ export class Renderer implements IPagedRenderer {
const element = append(root, $('.extension'));
const iconContainer = append(element, $('.icon-container'));
const icon = append(iconContainer, $('img.icon'));
- const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer);
+ const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer, false);
const details = append(element, $('.details'));
const headerContainer = append(details, $('.header-container'));
const header = append(headerContainer, $('.header'));
@@ -75,7 +75,7 @@ export class Renderer implements IPagedRenderer {
const version = append(header, $('span.version'));
const installCount = append(header, $('span.install-count'));
const ratings = append(header, $('span.ratings'));
- const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header);
+ const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header, false);
const description = append(details, $('.description.ellipsis'));
const footer = append(details, $('.footer'));
const author = append(footer, $('.author.ellipsis'));
@@ -98,6 +98,7 @@ export class Renderer implements IPagedRenderer {
reloadAction,
this.instantiationService.createInstance(InstallAction),
this.instantiationService.createInstance(RemoteInstallAction),
+ this.instantiationService.createInstance(LocalInstallAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
systemDisabledWarningAction,
this.instantiationService.createInstance(ManageExtensionAction)
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts
index 936c8f01ad..8e2c1b6365 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViewlet.ts
@@ -55,6 +55,9 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { RemoteAuthorityContext } from 'vs/workbench/common/contextkeys';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
+import { ILabelService } from 'vs/platform/label/common/label';
interface SearchInputEvent extends Event {
target: HTMLInputElement;
@@ -91,7 +94,9 @@ const viewIdNameMappings: { [id: string]: string } = {
export class ExtensionsViewletViewsContribution implements IWorkbenchContribution {
constructor(
- @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
+ @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
+ @ILabelService private readonly labelService: ILabelService,
+ @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService
) {
this.registerViews();
}
@@ -180,22 +185,32 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
*/
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
+ const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
+ const serverLabel = this.workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? this.labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
+ if (viewTitle && this.workbenchEnvironmentService.configuration.remoteAuthority) {
+ return `${serverLabel} - ${viewTitle}`;
+ }
+ return viewTitle ? viewTitle : serverLabel;
+ };
+ const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server);
+ const getOutdatedViewName = (): string => getViewName(localize('outdated', "Outdated"), server);
+ const onDidChangeServerLabel: EventOf = this.workbenchEnvironmentService.configuration.remoteAuthority ? EventOf.map(this.labelService.onDidChangeFormatters, () => undefined) : EventOf.None;
return [{
id: `extensions.${server.authority}.installed`,
- name: localize('installed', "Installed"),
- ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
+ get name() { return getInstalledViewName(); },
+ ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.outdated`,
- name: localize('outdated', "Outdated"),
- ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
+ get name() { return getOutdatedViewName(); },
+ ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getOutdatedViewName())] },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
weight: 100
}, {
id: `extensions.${server.authority}.default`,
- name: localize('installed', "Installed"),
- ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
+ get name() { return getInstalledViewName(); },
+ ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())] },
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
weight: 40,
order: 1
@@ -228,7 +243,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
ctorDescriptor: { ctor: RecommendedExtensionsView },
when: ContextKeyExpr.has('recommendedExtensions'),
weight: 50,
- canToggleVisibility: true,
order: 2
};
}
@@ -243,7 +257,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
ctorDescriptor: { ctor: WorkspaceRecommendedExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
weight: 50,
- canToggleVisibility: true,
order: 1
};
}
@@ -256,7 +269,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
ctorDescriptor: { ctor: EnabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
weight: 40,
- canToggleVisibility: true,
order: 1
};
}
@@ -269,7 +281,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
ctorDescriptor: { ctor: DisabledExtensionsView },
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')),
weight: 10,
- canToggleVisibility: true,
order: 3,
collapsed: true
};
@@ -282,8 +293,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: BuiltInExtensionsView },
when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100,
- canToggleVisibility: true
+ weight: 100
};
}
@@ -294,8 +304,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: BuiltInThemesExtensionsView },
when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100,
- canToggleVisibility: true
+ weight: 100
};
}
@@ -306,8 +315,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
name: viewIdNameMappings[id],
ctorDescriptor: { ctor: BuiltInBasicsExtensionsView },
when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100,
- canToggleVisibility: true
+ weight: 100
};
}
}
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts
index 477bdb8972..a3b4a9113a 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsViews.ts
@@ -16,7 +16,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { append, $, toggleClass } from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/electron-browser/extensionsList';
-import { IExtension, IExtensionsWorkbenchService } from '../common/extensions';
+import { IExtension, IExtensionsWorkbenchService, ExtensionState } from '../common/extensions';
import { Query } from '../common/extensionQuery';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -45,9 +45,7 @@ import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePa
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import product from 'vs/platform/product/node/product';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
-import { ILabelService } from 'vs/platform/label/common/label';
-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
-import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
+import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil';
class ExtensionsViewState extends Disposable implements IExtensionsViewState {
@@ -97,7 +95,7 @@ export class ExtensionsListView extends ViewletPanel {
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IExperimentService private readonly experimentService: IExperimentService,
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
- @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
+ @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
this.server = options.server;
@@ -340,11 +338,20 @@ export class ExtensionsListView extends ViewletPanel {
const isE1Running = running1 && this.extensionManagementServerService.getExtensionManagementServer(running1.extensionLocation) === e1.server;
const running2 = runningExtensionsById.get(ExtensionIdentifier.toKey(e2.identifier.id));
const isE2Running = running2 && this.extensionManagementServerService.getExtensionManagementServer(running2.extensionLocation) === e2.server;
- if ((isE1Running && isE2Running) || (!isE1Running && !isE2Running)) {
+ if ((isE1Running && isE2Running)) {
return e1.displayName.localeCompare(e2.displayName);
}
const isE1LanguagePackExtension = e1.local && isLanguagePackExtension(e1.local.manifest);
const isE2LanguagePackExtension = e2.local && isLanguagePackExtension(e2.local.manifest);
+ if (!isE1Running && !isE2Running) {
+ if (isE1LanguagePackExtension) {
+ return -1;
+ }
+ if (isE2LanguagePackExtension) {
+ return 1;
+ }
+ return e1.displayName.localeCompare(e2.displayName);
+ }
if ((isE1Running && isE2LanguagePackExtension) || (isE2Running && isE1LanguagePackExtension)) {
return e1.displayName.localeCompare(e2.displayName);
}
@@ -865,18 +872,11 @@ export class ExtensionsListView extends ViewletPanel {
}
}
-function getViewTitleForServer(viewTitle: string, server: IExtensionManagementServer, labelService: ILabelService, workbenchEnvironmentService: IWorkbenchEnvironmentService): string {
- const serverLabel = workbenchEnvironmentService.configuration.remoteAuthority === server.authority ? labelService.getHostLabel(REMOTE_HOST_SCHEME, server.authority) || server.label : server.label;
- if (viewTitle && workbenchEnvironmentService.configuration.remoteAuthority) {
- return `${serverLabel} - ${viewTitle}`;
- }
- return viewTitle ? viewTitle : serverLabel;
-}
-
export class ServerExtensionsView extends ExtensionsListView {
constructor(
server: IExtensionManagementServer,
+ onDidChangeTitle: Event,
options: ExtensionsListViewOptions,
@INotificationService notificationService: INotificationService,
@IKeybindingService keybindingService: IKeybindingService,
@@ -893,15 +893,11 @@ export class ServerExtensionsView extends ExtensionsListView {
@IExperimentService experimentService: IExperimentService,
@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
- @ILabelService labelService: ILabelService,
- @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
) {
- const viewTitle = options.title;
- options.title = getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService);
options.server = server;
super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, modeService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService);
- this.disposables.push(labelService.onDidChangeFormatters(() => this.updateTitle(getViewTitleForServer(viewTitle, server, labelService, workbenchEnvironmentService))));
+ this.disposables.push(onDidChangeTitle(title => this.updateTitle(title)));
}
async show(query: string): Promise> {
@@ -1044,6 +1040,12 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
private getRecommendationsToInstall(): Promise {
return this.tipsService.getWorkspaceRecommendations()
- .then(recommendations => recommendations.filter(({ extensionId }) => !this.extensionsWorkbenchService.local.some(i => areSameExtensions({ id: extensionId }, i.identifier))));
+ .then(recommendations => recommendations.filter(({ extensionId }) => {
+ const extension = this.extensionsWorkbenchService.local.filter(i => areSameExtensions({ id: extensionId }, i.identifier))[0];
+ if (!extension || !extension.local || extension.state !== ExtensionState.Installed) {
+ return true;
+ }
+ return isUIExtension(extension.local.manifest, this.configurationService) ? extension.server !== this.extensionManagementServerService.localExtensionManagementServer : extension.server !== this.extensionManagementServerService.remoteExtensionManagementServer;
+ }));
}
}
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts
index e565687756..6228de3bd3 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsWidgets.ts
@@ -5,7 +5,7 @@
import 'vs/css!./media/extensionsWidgets';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
-import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from '../common/extensions';
+import { IExtension, IExtensionsWorkbenchService, IExtensionContainer, ExtensionState } from '../common/extensions';
import { append, $, addClass } from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
@@ -148,34 +148,46 @@ export class TooltipWidget extends ExtensionWidget {
constructor(
private readonly parent: HTMLElement,
- private readonly extensionLabelAction: DisabledLabelAction,
+ private readonly disabledLabelAction: DisabledLabelAction,
private readonly recommendationWidget: RecommendationWidget,
- private readonly reloadAction: ReloadAction
+ private readonly reloadAction: ReloadAction,
+ @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
+ @ILabelService private readonly labelService: ILabelService,
+ @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService
) {
super();
this._register(Event.any(
- this.extensionLabelAction.onDidChange,
+ this.disabledLabelAction.onDidChange,
this.reloadAction.onDidChange,
- this.recommendationWidget.onDidChangeTooltip
+ this.recommendationWidget.onDidChangeTooltip,
+ this.labelService.onDidChangeFormatters
)(() => this.render()));
}
render(): void {
this.parent.title = '';
this.parent.removeAttribute('aria-label');
+ this.parent.title = this.getTooltip();
if (this.extension) {
- const title = this.getTitle();
- this.parent.title = title;
this.parent.setAttribute('aria-label', localize('extension-arialabel', "{0}. {1} Press enter for extension details.", this.extension.displayName));
}
}
- private getTitle(): string {
+ private getTooltip(): string {
+ if (!this.extension) {
+ return '';
+ }
if (this.reloadAction.enabled) {
return this.reloadAction.tooltip;
}
- if (this.extensionLabelAction.enabled) {
- return this.extensionLabelAction.label;
+ if (this.disabledLabelAction.label) {
+ return this.disabledLabelAction.label;
+ }
+ if (this.extension.local && this.extension.state === ExtensionState.Installed) {
+ if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
+ return localize('extension enabled on remote', "Extension is enabled on '{0}'", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority));
+ }
+ return localize('extension enabled locally', "Extension is enabled locally.");
}
return this.recommendationWidget.tooltip;
}
@@ -252,6 +264,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
constructor(
parent: HTMLElement,
+ private readonly tooltip: boolean,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
@@ -276,7 +289,7 @@ export class RemoteBadgeWidget extends ExtensionWidget {
return;
}
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
- this.remoteBadge = this.instantiationService.createInstance(RemoteBadge);
+ this.remoteBadge = this.instantiationService.createInstance(RemoteBadge, this.tooltip);
append(this.element, this.remoteBadge.element);
}
}
@@ -294,6 +307,7 @@ class RemoteBadge extends Disposable {
readonly element: HTMLElement;
constructor(
+ private readonly tooltip: boolean,
@ILabelService private readonly labelService: ILabelService,
@IThemeService private readonly themeService: IThemeService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@@ -320,12 +334,14 @@ class RemoteBadge extends Disposable {
this._register(this.themeService.onThemeChange(() => applyBadgeStyle()));
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => applyBadgeStyle()));
- const updateTitle = () => {
- if (this.element) {
- this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
- }
- };
- this._register(this.labelService.onDidChangeFormatters(() => updateTitle()));
- updateTitle();
+ if (this.tooltip) {
+ const updateTitle = () => {
+ if (this.element) {
+ this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
+ }
+ };
+ this._register(this.labelService.onDidChangeFormatters(() => updateTitle()));
+ updateTitle();
+ }
}
}
\ No newline at end of file
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css
index a275945e03..a6fc803419 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css
+++ b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionActions.css
@@ -7,6 +7,9 @@
padding: 0 5px;
outline-offset: 2px;
line-height: initial;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
.monaco-action-bar .action-item .action-label.clear-extensions {
@@ -35,7 +38,7 @@
.monaco-action-bar .action-item.disabled .action-label.extension-action.extension-editor-dropdown-action,
.monaco-action-bar .action-item.disabled .action-label.extension-action.reload,
.monaco-action-bar .action-item.disabled .action-label.disable-status.hide,
-.monaco-action-bar .action-item.disabled .action-label.disable-warning.hide,
+.monaco-action-bar .action-item.disabled .action-label.system-disable.hide,
.monaco-action-bar .action-item.disabled .action-label.extension-status-label.hide,
.monaco-action-bar .action-item.disabled .action-label.malicious-status.not-malicious {
display: none;
@@ -67,24 +70,33 @@
padding-left: 0;
}
-.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.disable-warning,
-.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.disable-warning {
- cursor: default;
- margin: 0.1em;
+.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .action-label.system-disable,
+.extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.system-disable {
+ margin: 0.15em;
}
-.monaco-action-bar .action-item .action-label.disable-warning.icon {
+.monaco-action-bar .action-item .action-label.system-disable.icon {
opacity: 1;
height: 18px;
width: 10px;
- background: url('status-warning.svg') center center no-repeat;
- margin-top: 0.15em
}
-.vs-dark .monaco-action-bar .action-item .action-label.disable-warning.icon {
+.monaco-action-bar .action-item .action-label.system-disable.warning.icon {
+ background: url('status-warning.svg') center center no-repeat;
+}
+
+.vs-dark .monaco-action-bar .action-item .action-label.system-disable.warning.icon {
background: url('status-warning-inverse.svg') center center no-repeat;
}
+.monaco-action-bar .action-item .action-label.system-disable.info.icon {
+ background: url('status-info.svg') center center no-repeat;
+}
+
+.vs-dark .monaco-action-bar .action-item .action-label.system-disable.info.icon {
+ background: url('status-info-inverse.svg') center center no-repeat;
+}
+
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label,
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status,
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status {
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionEditor.css
index 98667a6765..ba5b43fabf 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionEditor.css
+++ b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionEditor.css
@@ -41,11 +41,13 @@
line-height: 38px;
border-radius: 20px;
text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.extension-editor > .header > .icon-container .extension-remote-badge .octicon {
- font-size: 32px;
- vertical-align: middle;
+ font-size: 28px;
}
.extension-editor > .header > .details {
@@ -145,6 +147,9 @@
padding: 1px 6px;
}
+.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container > .action-item > .extension-action {
+ max-width: 300px;
+}
.extension-editor > .header > .details > .subtext-container {
display: block;
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionsViewlet.css
index 9d6e694a15..d21056d85d 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/media/extensionsViewlet.css
+++ b/src/vs/workbench/contrib/extensions/electron-browser/media/extensionsViewlet.css
@@ -113,6 +113,10 @@
text-align: center;
}
+.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container .extension-remote-badge > .octicon {
+ vertical-align: middle
+}
+
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header > .extension-remote-badge-container {
margin-left: 6px;
}
@@ -123,11 +127,13 @@
line-height: 14px;
border-radius: 20px;
text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge > .octicon {
- font-size: 13px;
- vertical-align: middle;
+ font-size: 12px;
}
.extensions-viewlet.narrow > .extensions .extension > .icon-container,
@@ -224,6 +230,14 @@
flex-wrap: wrap-reverse;
}
+.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container .extension-action {
+ max-width: 150px;
+}
+
+.extensions-viewlet.narrow > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container .extension-action {
+ max-width: 100px;
+}
+
.extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar .action-label {
margin-top: 0.3em;
margin-left: 0.3em;
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/status-info-inverse.svg b/src/vs/workbench/contrib/extensions/electron-browser/media/status-info-inverse.svg
new file mode 100644
index 0000000000..d38c363e0e
--- /dev/null
+++ b/src/vs/workbench/contrib/extensions/electron-browser/media/status-info-inverse.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/media/status-info.svg b/src/vs/workbench/contrib/extensions/electron-browser/media/status-info.svg
new file mode 100644
index 0000000000..6e2e22f67b
--- /dev/null
+++ b/src/vs/workbench/contrib/extensions/electron-browser/media/status-info.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts
index 081ad1d08b..122c32e31e 100644
--- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts
+++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts
@@ -379,12 +379,12 @@ export class RuntimeExtensionsEditor extends BaseEditor {
if (element.description.extensionLocation.scheme !== 'file') {
const el = $('span');
- el.innerHTML = renderOcticons(`$(rss) ${element.description.extensionLocation.authority}`);
+ el.innerHTML = renderOcticons(`$(remote) ${element.description.extensionLocation.authority}`);
data.msgContainer.appendChild(el);
const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._environmentService.configuration.remoteAuthority);
if (hostLabel) {
- el.innerHTML = renderOcticons(`$(rss) ${hostLabel}`);
+ el.innerHTML = renderOcticons(`$(remote) ${hostLabel}`);
}
}
diff --git a/src/vs/workbench/contrib/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/node/extensionsWorkbenchService.ts
index aa60ee7c50..644a998578 100644
--- a/src/vs/workbench/contrib/extensions/node/extensionsWorkbenchService.ts
+++ b/src/vs/workbench/contrib/extensions/node/extensionsWorkbenchService.ts
@@ -36,7 +36,7 @@ import * as resources from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IFileService } from 'vs/platform/files/common/files';
-import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions';
+import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
// {{SQL CARBON EDIT}}
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
@@ -605,15 +605,19 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
get local(): IExtension[] {
- const result = [...this.localExtensions.local];
- if (!this.remoteExtensions) {
- return result;
- }
- result.push(...this.remoteExtensions.local);
+ const result = [...this.installed];
const byId = groupByExtension(result, r => r.identifier);
return byId.reduce((result, extensions) => { result.push(this.getPrimaryExtension(extensions)); return result; }, []);
}
+ get installed(): IExtension[] {
+ const result = [...this.localExtensions.local];
+ if (this.remoteExtensions) {
+ result.push(...this.remoteExtensions.local);
+ }
+ return result;
+ }
+
get outdated(): IExtension[] {
const allLocal = [...this.localExtensions.local];
if (this.remoteExtensions) {
@@ -855,7 +859,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
}
return this.installWithProgress(async () => {
- // {{SQL CARBON EDIT}}
+ // {{SQL CARBON EDIT}} remove extensionservice install from gallery
if (extensionPolicy === ExtensionsPolicy.allowMicrosoft) {
if (extension.publisherDisplayName === 'Microsoft') {
await this.downloadOrBrowse(extension).then(() => this.checkAndEnableDisabledDependencies(gallery.identifier));
@@ -916,7 +920,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' with version '{1}' as it is not compatible with Azure Data Studio.", extension.gallery!.identifier.id, version)));
}
return this.installWithProgress(async () => {
- await this.extensionService.installFromGallery(gallery);
+ const extensionService = extension.server && extension.local && !isLanguagePackExtension(extension.local.manifest) ? extension.server.extensionManagementService : this.extensionService;
+ await extensionService.installFromGallery(gallery);
if (extension.latestVersion !== version) {
this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version));
}
diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts
index 5fa7f0279e..8a2744ee59 100644
--- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts
+++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts
@@ -15,6 +15,7 @@ import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
+import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
/**
* An implementation of editor for binary files like images.
@@ -29,7 +30,8 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
@IWindowsService private readonly windowsService: IWindowsService,
@IEditorService private readonly editorService: IEditorService,
@IStorageService storageService: IStorageService,
- @ITextFileService textFileService: ITextFileService
+ @ITextFileService textFileService: ITextFileService,
+ @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
) {
super(
BinaryFileEditor.ID,
@@ -40,7 +42,8 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
telemetryService,
themeService,
textFileService,
- storageService
+ environmentService,
+ storageService,
);
}
diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts
index a001110d46..c768f8fe74 100644
--- a/src/vs/workbench/contrib/files/browser/files.contribution.ts
+++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts
@@ -38,6 +38,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService';
import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
+import { Schemas } from 'vs/base/common/network';
// Viewlet Action
export class OpenExplorerViewletAction extends ShowViewletAction {
@@ -59,7 +60,7 @@ class FileUriLabelContribution implements IWorkbenchContribution {
constructor(@ILabelService labelService: ILabelService) {
labelService.registerFormatter({
- scheme: 'file',
+ scheme: Schemas.file,
formatting: {
label: '${authority}${path}',
separator: sep,
@@ -307,6 +308,7 @@ configurationRegistry.registerConfiguration({
},
'files.hotExit': {
'type': 'string',
+ 'scope': ConfigurationScope.APPLICATION,
'enum': [HotExitConfiguration.OFF, HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE],
'default': HotExitConfiguration.ON_EXIT,
'markdownEnumDescriptions': [
diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts
index aef42b2a92..3deffe16ca 100644
--- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts
+++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts
@@ -136,14 +136,15 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
const isReadonly = fileOperationError.fileOperationResult === FileOperationResult.FILE_READ_ONLY;
const triedToMakeWriteable = isReadonly && fileOperationError.options && (fileOperationError.options as IWriteTextFileOptions).overwriteReadonly;
const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED;
+ const canHandlePermissionOrReadonlyErrors = resource.scheme === Schemas.file; // https://github.com/Microsoft/vscode/issues/48659
- // Save Elevated (cannot write elevated https://github.com/Microsoft/vscode/issues/48659)
- if (resource.scheme === Schemas.file && (isPermissionDenied || triedToMakeWriteable)) {
+ // Save Elevated
+ if (canHandlePermissionOrReadonlyErrors && (isPermissionDenied || triedToMakeWriteable)) {
actions.primary!.push(this.instantiationService.createInstance(SaveElevatedAction, model, triedToMakeWriteable));
}
- // Overwrite (cannot overwrite readonly https://github.com/Microsoft/vscode/issues/48659)
- else if (resource.scheme === Schemas.file && isReadonly) {
+ // Overwrite
+ else if (canHandlePermissionOrReadonlyErrors && isReadonly) {
actions.primary!.push(this.instantiationService.createInstance(OverwriteReadonlyAction, model));
}
@@ -158,13 +159,14 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
// Discard
actions.primary!.push(this.instantiationService.createInstance(ExecuteCommandAction, REVERT_FILE_COMMAND_ID, nls.localize('discard', "Discard")));
- if (isReadonly) {
+ // Message
+ if (canHandlePermissionOrReadonlyErrors && isReadonly) {
if (triedToMakeWriteable) {
message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.", basename(resource)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.", basename(resource));
} else {
message = nls.localize('readonlySaveError', "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.", basename(resource));
}
- } else if (isPermissionDenied) {
+ } else if (canHandlePermissionOrReadonlyErrors && isPermissionDenied) {
message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", basename(resource)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", basename(resource));
} else {
message = nls.localize('genericSaveError', "Failed to save '{0}': {1}", basename(resource), toErrorMessage(error, false));
@@ -246,7 +248,7 @@ class ResolveSaveConflictAction extends Action {
return this.editorService.openEditor(
{
- leftResource: URI.from({ scheme: CONFLICT_RESOLUTION_SCHEME, path: resource.fsPath }),
+ leftResource: resource.with({ scheme: CONFLICT_RESOLUTION_SCHEME }),
rightResource: resource,
label: editorLabel,
options: { pinned: true }
diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts
index 9fc57ae620..0f31b7b564 100644
--- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts
+++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts
@@ -50,7 +50,7 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
return commandService.executeCommand('editor.action.formatDocument');
} else {
const langName = model.getLanguageIdentifier().language;
- const message = nls.localize('no.rovider', "There is no formatter for '{0}'-files installed.", langName);
+ const message = nls.localize('no.provider', "There is no formatter for '{0}'-files installed.", langName);
const choice = {
label: nls.localize('install.formatter', "Install Formatter..."),
run: () => showExtensionQuery(viewletService, `category:formatters ${langName}`)
diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts
index 580149e3ef..7fb658fe88 100644
--- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts
+++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts
@@ -159,6 +159,11 @@ export const tocData: ITOCEntry = {
id: 'features/comments',
label: localize('comments', "Comments"),
settings: ['comments.*']
+ },
+ {
+ id: 'features/remote',
+ label: localize('remote', "Remote"),
+ settings: ['remote.*']
}
]
},
diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts
index 27a011eda5..8fa236cd41 100644
--- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts
+++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts
@@ -418,14 +418,14 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
.then(() => {
const remoteAuthority = environmentService.configuration.remoteAuthority;
const hostLabel = labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority) || remoteAuthority;
- const label = nls.localize('openRemoteSettings', "Open User Settings ({0})", hostLabel);
+ const label = nls.localize('openRemoteSettings', "Open Remote Settings ({0})", hostLabel);
CommandsRegistry.registerCommand(OpenRemoteSettingsAction.ID, serviceAccessor => {
serviceAccessor.get(IInstantiationService).createInstance(OpenRemoteSettingsAction, OpenRemoteSettingsAction.ID, label).run();
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: OpenRemoteSettingsAction.ID,
- title: { value: label, original: `Preferences: Open User Settings (${hostLabel})` },
+ title: { value: label, original: `Preferences: Open Remote Settings (${hostLabel})` },
category: nls.localize('preferencesCategory', "Preferences")
},
when: RemoteAuthorityContext.notEqualsTo('')
diff --git a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts
index dd0ac81aa1..0b29e17068 100644
--- a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts
+++ b/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts
@@ -205,7 +205,7 @@ export class SettingsEditor2 extends BaseEditor {
this.updateStyles();
}
- setInput(input: SettingsEditor2Input, options: SettingsEditorOptions, token: CancellationToken): Promise {
+ setInput(input: SettingsEditor2Input, options: SettingsEditorOptions | null, token: CancellationToken): Promise {
this.inSettingsEditorContextKey.set(true);
return super.setInput(input, options, token)
.then(() => new Promise(process.nextTick)) // Force setInput to be async
@@ -213,10 +213,10 @@ export class SettingsEditor2 extends BaseEditor {
return this.render(token);
})
.then(() => {
+ options = options || SettingsEditorOptions.create({});
+
if (!this.viewState.settingsTarget) {
- if (!options) {
- options = SettingsEditorOptions.create({ target: ConfigurationTarget.USER_LOCAL });
- } else if (!options.target) {
+ if (!options.target) {
options.target = ConfigurationTarget.USER_LOCAL;
}
}
@@ -1065,7 +1065,7 @@ export class SettingsEditor2 extends BaseEditor {
this.searchInProgress = null;
}
- this.viewState.filterToCategory = undefined;
+ this.tocTree.setFocus([]);
this.tocTreeModel.currentSearchModel = this.searchResultModel;
this.onSearchModeToggled();
@@ -1206,8 +1206,7 @@ export class SettingsEditor2 extends BaseEditor {
this.tocTreeModel.update();
}
- this.tocTree.setSelection([]);
- this.viewState.filterToCategory = undefined;
+ this.tocTree.setFocus([]);
this.tocTree.expandAll();
this.renderTree(undefined, true);
diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts
index 38c1d8928a..e7dca4560f 100644
--- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts
+++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts
@@ -26,12 +26,12 @@ const id = 'workbench.action.openSnippets';
namespace ISnippetPick {
export function is(thing: object): thing is ISnippetPick {
- return thing && typeof (thing).filepath === 'string';
+ return thing && URI.isUri((thing).filepath);
}
}
interface ISnippetPick extends IQuickPickItem {
- filepath: string;
+ filepath: URI;
hint?: true;
}
@@ -71,7 +71,7 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
existing.push({
label: basename(file.location.fsPath),
- filepath: file.location.fsPath,
+ filepath: file.location,
description: names.size === 0
? nls.localize('global.scope', "(global)")
: nls.localize('global.1', "({0})", values(names).join(', '))
@@ -83,7 +83,7 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
existing.push({
label: basename(file.location.fsPath),
description: `(${modeService.getLanguageName(mode)})`,
- filepath: file.location.fsPath
+ filepath: file.location
});
seen.add(mode);
}
@@ -96,15 +96,15 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir
future.push({
label: mode,
description: `(${label})`,
- filepath: join(dir, `${mode}.json`),
+ filepath: URI.file(join(dir, `${mode}.json`)),
hint: true
});
}
}
existing.sort((a, b) => {
- let a_ext = extname(a.filepath);
- let b_ext = extname(b.filepath);
+ let a_ext = extname(a.filepath.path);
+ let b_ext = extname(b.filepath.path);
if (a_ext === b_ext) {
return a.label.localeCompare(b.label);
} else if (a_ext === '.code-snippets') {
@@ -165,7 +165,7 @@ async function createSnippetFile(scope: string, defaultPath: URI, windowService:
}
async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService, textFileService: ITextFileService) {
- if (await fileService.exists(URI.file(pick.filepath))) {
+ if (await fileService.exists(pick.filepath)) {
return;
}
const contents = [
@@ -185,7 +185,7 @@ async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileS
'\t// }',
'}'
].join('\n');
- await textFileService.write(URI.file(pick.filepath), contents);
+ await textFileService.write(pick.filepath, contents);
}
CommandsRegistry.registerCommand(id, async (accessor): Promise => {
@@ -240,7 +240,7 @@ CommandsRegistry.registerCommand(id, async (accessor): Promise => {
if (pick.hint) {
await createLanguageSnippetFile(pick, fileService, textFileService);
}
- return opener.open(URI.file(pick.filepath));
+ return opener.open(pick.filepath);
}
});
diff --git a/src/vs/workbench/contrib/stats/node/workspaceStats.ts b/src/vs/workbench/contrib/stats/node/workspaceStats.ts
index 93bf7e7184..9447f01882 100644
--- a/src/vs/workbench/contrib/stats/node/workspaceStats.ts
+++ b/src/vs/workbench/contrib/stats/node/workspaceStats.ts
@@ -246,8 +246,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
/* __GDPR__FRAGMENT__
"WorkspaceTags" : {
- "workbench.filesToOpen" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
- "workbench.filesToCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
+ "workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbench.filesToDiff" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workspace.id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"workspace.roots" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
@@ -352,9 +351,8 @@ export class WorkspaceStats implements IWorkbenchContribution {
tags['workspace.id'] = workspaceId;
- const { filesToOpen, filesToCreate, filesToDiff } = configuration;
- tags['workbench.filesToOpen'] = filesToOpen && filesToOpen.length || 0;
- tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || 0;
+ const { filesToOpenOrCreate, filesToDiff } = configuration;
+ tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0;
tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || 0;
const isEmpty = state === WorkbenchState.EMPTY;
@@ -586,11 +584,9 @@ export class WorkspaceStats implements IWorkbenchContribution {
return folder && [folder];
}
- private findFolder({ filesToOpen, filesToCreate, filesToDiff }: IWindowConfiguration): URI | undefined {
- if (filesToOpen && filesToOpen.length) {
- return this.parentURI(filesToOpen[0].fileUri);
- } else if (filesToCreate && filesToCreate.length) {
- return this.parentURI(filesToCreate[0].fileUri);
+ private findFolder({ filesToOpenOrCreate, filesToDiff }: IWindowConfiguration): URI | undefined {
+ if (filesToOpenOrCreate && filesToOpenOrCreate.length) {
+ return this.parentURI(filesToOpenOrCreate[0].fileUri);
} else if (filesToDiff && filesToDiff.length) {
return this.parentURI(filesToDiff[0].fileUri);
}
diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts
index a8caad21e1..219d3e0fbb 100644
--- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts
+++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts
@@ -36,7 +36,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
) {
super();
- const { filesToOpen, filesToCreate, filesToDiff } = environmentService.configuration;
+ const { filesToOpenOrCreate, filesToDiff } = environmentService.configuration;
const activeViewlet = viewletService.getActiveViewlet();
/* __GDPR__
@@ -47,8 +47,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
"windowSize.outerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"windowSize.outerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"emptyWorkbench": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
- "workbench.filesToOpen": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
- "workbench.filesToCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
+ "workbench.filesToOpenOrCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbench.filesToDiff": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"customKeybindingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"theme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
@@ -64,8 +63,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
userAgent: navigator.userAgent,
windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth },
emptyWorkbench: contextService.getWorkbenchState() === WorkbenchState.EMPTY,
- 'workbench.filesToOpen': filesToOpen && filesToOpen.length || 0,
- 'workbench.filesToCreate': filesToCreate && filesToCreate.length || 0,
+ 'workbench.filesToOpenOrCreate': filesToOpenOrCreate && filesToOpenOrCreate.length || 0,
'workbench.filesToDiff': filesToDiff && filesToDiff.length || 0,
customKeybindingsCount: keybindingsService.customKeybindingsCount(),
theme: themeService.getColorTheme().id,
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
index 6a152373f8..327f071ab5 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts
@@ -339,7 +339,7 @@ export class CreateNewTerminalAction extends Action {
// Don't create the instance if the workspace picker was canceled
return null;
}
- return this.terminalService.createTerminal({ cwd: workspace.uri.fsPath }, true);
+ return this.terminalService.createTerminal({ cwd: workspace.uri }, true);
});
}
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
index 412693c60e..61d21effd2 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts
@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { Schemas } from 'vs/base/common/network';
-import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
+import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IProductService } from 'vs/platform/product/common/product';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
@@ -131,7 +131,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
});
}
- const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined);
+ const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot();
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper);
} else {
this._process = this._launchProcess(shellLaunchConfig, cols, rows);
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts
index 3e982d3598..716406ab17 100644
--- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts
+++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts
@@ -34,24 +34,26 @@ export function registerFileProtocol(
getRoots: () => ReadonlyArray
) {
contents.session.protocol.registerBufferProtocol(protocol, (request, callback: any) => {
- if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
- const requestUri = URI.parse(request.url);
- const redirectedUri = URI.from({
- scheme: REMOTE_HOST_SCHEME,
- authority: extensionLocation.authority,
- path: '/vscode-resource',
- query: JSON.stringify({
- requestResourcePath: requestUri.path
- })
- });
- resolveContent(textFileService, redirectedUri, getMimeType(requestUri), callback);
- return;
- }
-
const requestPath = URI.parse(request.url).path;
const normalizedPath = URI.file(requestPath);
for (const root of getRoots()) {
- if (startsWith(normalizedPath.fsPath, root.fsPath + sep)) {
+ if (!startsWith(normalizedPath.fsPath, root.fsPath + sep)) {
+ continue;
+ }
+
+ if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
+ const requestUri = URI.parse(request.url);
+ const redirectedUri = URI.from({
+ scheme: REMOTE_HOST_SCHEME,
+ authority: extensionLocation.authority,
+ path: '/vscode-resource',
+ query: JSON.stringify({
+ requestResourcePath: requestUri.path
+ })
+ });
+ resolveContent(textFileService, redirectedUri, getMimeType(requestUri), callback);
+ return;
+ } else {
resolveContent(textFileService, normalizedPath, getMimeType(normalizedPath), callback);
return;
}
diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts
index 158e117120..f95b5526e7 100644
--- a/src/vs/workbench/electron-browser/main.contribution.ts
+++ b/src/vs/workbench/electron-browser/main.contribution.ts
@@ -652,6 +652,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow
'type': 'boolean',
'default': true,
'description': nls.localize('autoDetectHighContrast', "If enabled, will automatically change to high contrast theme if Windows is using a high contrast theme, and to dark theme when switching away from a Windows high contrast theme."),
+ 'scope': ConfigurationScope.APPLICATION,
'included': isWindows
},
'window.doubleClickIconToClose': {
@@ -678,6 +679,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow
'type': 'boolean',
'default': true,
'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."),
+ 'scope': ConfigurationScope.APPLICATION,
'included': isMacintosh
},
'window.clickThroughInactive': {
diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts
index e12bf43555..db235c946e 100644
--- a/src/vs/workbench/electron-browser/main.ts
+++ b/src/vs/workbench/electron-browser/main.ts
@@ -91,7 +91,7 @@ class CodeRendererMain extends Disposable {
const filesToWait = this.configuration.filesToWait;
const filesToWaitPaths = filesToWait && filesToWait.paths;
- [filesToWaitPaths, this.configuration.filesToOpen, this.configuration.filesToCreate, this.configuration.filesToDiff].forEach(paths => {
+ [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff].forEach(paths => {
if (Array.isArray(paths)) {
paths.forEach(path => {
if (path.fileUri) {
diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts
index ac55545d8e..373f1ddedb 100644
--- a/src/vs/workbench/electron-browser/window.ts
+++ b/src/vs/workbench/electron-browser/window.ts
@@ -11,10 +11,10 @@ import * as DOM from 'vs/base/browser/dom';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction, Action } from 'vs/base/common/actions';
import { IFileService } from 'vs/platform/files/common/files';
-import { toResource, IUntitledResourceInput, SideBySideEditor } from 'vs/workbench/common/editor';
+import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor';
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
-import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows';
+import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
@@ -468,20 +468,16 @@ export class ElectronWindow extends Disposable {
this.workspaceEditingService.addFolders(foldersToAdd);
}
- private onOpenFiles(request: IOpenFileRequest): void {
+ private async onOpenFiles(request: IOpenFileRequest): Promise {
const inputs: IResourceEditor[] = [];
const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2));
- if (!diffMode && request.filesToOpen) {
- inputs.push(...this.toInputs(request.filesToOpen, false));
- }
-
- if (!diffMode && request.filesToCreate) {
- inputs.push(...this.toInputs(request.filesToCreate, true));
+ if (!diffMode && request.filesToOpenOrCreate) {
+ inputs.push(...(await pathsToEditors(request.filesToOpenOrCreate, this.fileService)));
}
if (diffMode && request.filesToDiff) {
- inputs.push(...this.toInputs(request.filesToDiff, false));
+ inputs.push(...(await pathsToEditors(request.filesToDiff, this.fileService)));
}
if (inputs.length) {
@@ -521,27 +517,6 @@ export class ElectronWindow extends Disposable {
});
}
- private toInputs(paths: IPathData[], isNew: boolean): IResourceEditor[] {
- return paths.map(p => {
- const resource = URI.revive(p.fileUri);
- let input: IResourceInput | IUntitledResourceInput;
- if (isNew) {
- input = { filePath: resource!.fsPath, options: { pinned: true } };
- } else {
- input = { resource, options: { pinned: true } };
- }
-
- if (!isNew && typeof p.lineNumber === 'number' && typeof p.columnNumber === 'number') {
- input.options!.selection = {
- startLineNumber: p.lineNumber,
- startColumn: p.columnNumber
- };
- }
-
- return input;
- });
- }
-
dispose(): void {
this.touchBarDisposables = dispose(this.touchBarDisposables);
diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
index 33f2b19c62..a16e424f52 100644
--- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
+++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts
@@ -34,6 +34,7 @@ interface FileQuickPickItem extends IQuickPickItem {
enum UpdateResult {
Updated,
+ Updating,
NotUpdated,
InvalidPath
}
@@ -51,6 +52,7 @@ export class RemoteFileDialog {
private allowFolderSelection: boolean;
private remoteAuthority: string | undefined;
private requiresTrailing: boolean;
+ private trailing: string | undefined;
private scheme: string = REMOTE_HOST_SCHEME;
private contextKey: IContextKey;
private userEnteredPathSegment: string;
@@ -149,7 +151,6 @@ export class RemoteFileDialog {
this.allowFileSelection = !!this.options.canSelectFiles;
this.hidden = false;
let homedir: URI = this.options.defaultUri ? this.options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri;
- let trailing: string | undefined;
let stat: IFileStat | undefined;
let ext: string = resources.extname(homedir);
if (this.options.defaultUri) {
@@ -160,14 +161,14 @@ export class RemoteFileDialog {
}
if (!stat || !stat.isDirectory) {
homedir = resources.dirname(this.options.defaultUri);
- trailing = resources.basename(this.options.defaultUri);
+ this.trailing = resources.basename(this.options.defaultUri);
}
// append extension
if (isSave && !ext && this.options.filters) {
for (let i = 0; i < this.options.filters.length; i++) {
if (this.options.filters[i].extensions[0] !== '*') {
ext = '.' + this.options.filters[i].extensions[0];
- trailing = trailing ? trailing + ext : ext;
+ this.trailing = this.trailing ? this.trailing + ext : ext;
break;
}
}
@@ -176,6 +177,7 @@ export class RemoteFileDialog {
return new Promise(async (resolve) => {
this.filePickBox = this.quickInputService.createQuickPick();
+ this.filePickBox.busy = true;
this.filePickBox.matchOnLabel = false;
this.filePickBox.autoFocusOnList = false;
this.filePickBox.ignoreFocusOut = true;
@@ -221,6 +223,7 @@ export class RemoteFileDialog {
this.options.availableFileSystems.shift();
}
this.options.defaultUri = undefined;
+ this.filePickBox.hide();
if (this.requiresTrailing) {
return this.fileDialogService.showSaveDialog(this.options).then(result => {
doResolve(this, result);
@@ -264,7 +267,7 @@ export class RemoteFileDialog {
// onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything
if (this.isChangeFromUser()) {
// If the user has just entered more bad path, don't change anything
- if (value !== this.constructFullUserPath() && !this.isBadSubpath(value)) {
+ if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) {
this.filePickBox.validationMessage = undefined;
const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value));
let updated: UpdateResult = UpdateResult.NotUpdated;
@@ -288,12 +291,13 @@ export class RemoteFileDialog {
this.filePickBox.show();
this.contextKey.set(true);
- await this.updateItems(homedir, trailing);
- if (trailing) {
- this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - ext.length];
+ await this.updateItems(homedir, this.trailing);
+ if (this.trailing) {
+ this.filePickBox.valueSelection = [this.filePickBox.value.length - this.trailing.length, this.filePickBox.value.length - ext.length];
} else {
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
}
+ this.filePickBox.busy = false;
});
}
@@ -302,7 +306,7 @@ export class RemoteFileDialog {
}
private isChangeFromUser(): boolean {
- if ((this.filePickBox.value === this.pathAppend(this.currentFolder, this.userEnteredPathSegment + this.autoCompletePathSegment))
+ if (equalsIgnoreCase(this.filePickBox.value, this.pathAppend(this.currentFolder, this.userEnteredPathSegment + this.autoCompletePathSegment))
&& (this.activeItem === (this.filePickBox.activeItems ? this.filePickBox.activeItems[0] : undefined))) {
return false;
}
@@ -314,6 +318,7 @@ export class RemoteFileDialog {
}
private async onDidAccept(): Promise {
+ this.filePickBox.busy = true;
let resolveValue: URI | undefined;
let navigateValue: URI | undefined;
const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value;
@@ -351,21 +356,25 @@ export class RemoteFileDialog {
if (resolveValue) {
resolveValue = this.addPostfix(resolveValue);
if (await this.validate(resolveValue)) {
- return Promise.resolve(resolveValue);
+ this.filePickBox.busy = false;
+ return resolveValue;
}
} else if (navigateValue) {
- // Try to navigate into the folder
- await this.updateItems(navigateValue);
+ // Try to navigate into the folder.
+ await this.updateItems(navigateValue, this.trailing);
} else {
// validation error. Path does not exist.
}
- return Promise.resolve(undefined);
+ this.filePickBox.busy = false;
+ return undefined;
}
private async tryUpdateItems(value: string, valueUri: URI): Promise {
- if (value[value.length - 1] === '~') {
- await this.updateItems(this.userHome);
+ if (this.filePickBox.busy) {
this.badPath = undefined;
+ return UpdateResult.Updating;
+ } else if (value[value.length - 1] === '~') {
+ await this.updateItems(this.userHome);
return UpdateResult.Updated;
} else if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) {
let stat: IFileStat | undefined;
@@ -409,7 +418,7 @@ export class RemoteFileDialog {
const inputBasename = resources.basename(this.remoteUriFrom(value));
// Make sure that the folder whose children we are currently viewing matches the path in the input
const userPath = this.constructFullUserPath();
- if (userPath === value.substring(0, userPath.length)) {
+ if (equalsIgnoreCase(userPath, value.substring(0, userPath.length))) {
let hasMatch = false;
for (let i = 0; i < this.filePickBox.items.length; i++) {
const item = this.filePickBox.items[i];
@@ -424,7 +433,7 @@ export class RemoteFileDialog {
this.filePickBox.activeItems = [];
}
} else {
- if (inputBasename !== resources.basename(this.currentFolder)) {
+ if (!equalsIgnoreCase(inputBasename, resources.basename(this.currentFolder))) {
this.userEnteredPathSegment = inputBasename;
} else {
this.userEnteredPathSegment = '';
@@ -442,24 +451,34 @@ export class RemoteFileDialog {
}
const itemBasename = quickPickItem.label;
// Either force the autocomplete, or the old value should be one smaller than the new value and match the new value.
- if (!force && (itemBasename.length >= startingBasename.length) && equalsIgnoreCase(itemBasename.substr(0, startingBasename.length), startingBasename)) {
+ if (itemBasename === '..') {
+ // Don't match on the up directory item ever.
+ this.userEnteredPathSegment = startingValue;
+ this.autoCompletePathSegment = '';
+ this.activeItem = quickPickItem;
+ if (force) {
+ // clear any selected text
+ this.insertText(this.userEnteredPathSegment, '');
+ }
+ return false;
+ } else if (!force && (itemBasename.length >= startingBasename.length) && equalsIgnoreCase(itemBasename.substr(0, startingBasename.length), startingBasename)) {
this.userEnteredPathSegment = startingBasename;
this.activeItem = quickPickItem;
// Changing the active items will trigger the onDidActiveItemsChanged. Clear the autocomplete first, then set it after.
this.autoCompletePathSegment = '';
this.filePickBox.activeItems = [quickPickItem];
- this.autoCompletePathSegment = itemBasename.substr(startingBasename.length);
+ this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename.substr(startingBasename.length));
this.insertText(startingValue + this.autoCompletePathSegment, this.autoCompletePathSegment);
this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length];
return true;
- } else if (force && (quickPickItem.label !== (this.userEnteredPathSegment + this.autoCompletePathSegment))) {
+ } else if (force && (!equalsIgnoreCase(quickPickItem.label, (this.userEnteredPathSegment + this.autoCompletePathSegment)))) {
this.userEnteredPathSegment = '';
- this.autoCompletePathSegment = itemBasename;
+ this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename);
this.activeItem = quickPickItem;
this.filePickBox.valueSelection = [this.pathFromUri(this.currentFolder, true).length, this.filePickBox.value.length];
// use insert text to preserve undo buffer
- this.insertText(this.pathAppend(this.currentFolder, itemBasename), itemBasename);
- this.filePickBox.valueSelection = [this.filePickBox.value.length - itemBasename.length, this.filePickBox.value.length];
+ this.insertText(this.pathAppend(this.currentFolder, this.autoCompletePathSegment), this.autoCompletePathSegment);
+ this.filePickBox.valueSelection = [this.filePickBox.value.length - this.autoCompletePathSegment.length, this.filePickBox.value.length];
return true;
} else {
this.userEnteredPathSegment = startingBasename;
@@ -506,21 +525,24 @@ export class RemoteFileDialog {
return ((path.length > 1) && this.endsWithSlash(path)) ? path.substr(0, path.length - 1) : path;
}
- private yesNoPrompt(message: string): Promise {
+ private yesNoPrompt(uri: URI, message: string): Promise {
interface YesNoItem extends IQuickPickItem {
value: boolean;
}
const prompt = this.quickInputService.createQuickPick();
- const no = nls.localize('remoteFileDialog.no', 'No');
- prompt.items = [{ label: no, value: false }, { label: nls.localize('remoteFileDialog.yes', 'Yes'), value: true }];
prompt.title = message;
- prompt.placeholder = no;
+ prompt.ignoreFocusOut = true;
+ prompt.ok = true;
+ prompt.customButton = true;
+ prompt.customLabel = nls.localize('remoteFileDialog.cancel', 'Cancel');
+ prompt.value = this.pathFromUri(uri);
+
let isResolving = false;
return new Promise(resolve => {
prompt.onDidAccept(() => {
isResolving = true;
prompt.hide();
- resolve(prompt.selectedItems ? prompt.selectedItems[0].value : false);
+ resolve(true);
});
prompt.onDidHide(() => {
if (!isResolving) {
@@ -531,6 +553,12 @@ export class RemoteFileDialog {
this.filePickBox.items = this.filePickBox.items;
prompt.dispose();
});
+ prompt.onDidChangeValue(() => {
+ prompt.hide();
+ });
+ prompt.onDidCustom(() => {
+ prompt.hide();
+ });
prompt.show();
});
}
@@ -554,7 +582,7 @@ export class RemoteFileDialog {
// Replacing a file.
// Show a yes/no prompt
const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri));
- return this.yesNoPrompt(message);
+ return this.yesNoPrompt(uri, message);
} else if (!this.isValidBaseName(resources.basename(uri))) {
// Filename not allowed
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.');
@@ -587,15 +615,24 @@ export class RemoteFileDialog {
this.userEnteredPathSegment = trailing ? trailing : '';
this.autoCompletePathSegment = '';
const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true);
- this.currentFolder = this.remoteUriFrom(this.pathFromUri(newFolder, true));
+ const oldFolder = this.currentFolder;
+ const newFolderPath = this.pathFromUri(newFolder, true);
+ this.currentFolder = this.remoteUriFrom(newFolderPath);
return this.createItems(this.currentFolder).then(items => {
this.filePickBox.items = items;
if (this.allowFolderSelection) {
this.filePickBox.activeItems = [];
}
if (!equalsIgnoreCase(this.filePickBox.value, newValue)) {
- this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
- this.insertText(newValue, newValue);
+ // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory.
+ if (!equalsIgnoreCase(this.filePickBox.value.substring(0, newValue.length), newValue)) {
+ this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
+ this.insertText(newValue, newValue);
+ } else if (equalsIgnoreCase(this.pathFromUri(resources.dirname(oldFolder), true), newFolderPath)) {
+ // This is the case where the user went up one dir. We need to make sure that we remove the final dir.
+ this.filePickBox.valueSelection = [newFolderPath.length, this.filePickBox.value.length];
+ this.insertText(newValue, '');
+ }
}
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.filePickBox.busy = false;
diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts
index 95766b74af..58162b8e58 100644
--- a/src/vs/workbench/services/editor/browser/editorService.ts
+++ b/src/vs/workbench/services/editor/browser/editorService.ts
@@ -530,16 +530,11 @@ export class EditorService extends Disposable implements EditorServiceImpl {
// Untitled file support
const untitledInput = input;
- if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === Schemas.untitled)) {
+ if (untitledInput.forceUntitled || !untitledInput.resource || (untitledInput.resource && untitledInput.resource.scheme === Schemas.untitled)) {
// {{SQL CARBON EDIT}}
let modeId: string = untitledInput.language ? untitledInput.language : getFileMode(this.instantiationService, untitledInput.resource);
- return convertEditorInput(this.untitledEditorService.createOrGet(
- untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource,
- modeId,
- untitledInput.contents,
- untitledInput.encoding
- ), undefined, this.instantiationService);
+ return convertEditorInput(this.untitledEditorService.createOrGet(untitledInput.resource, modeId, untitledInput.contents, untitledInput.encoding), undefined, this.instantiationService);
}
// Resource Editor Support
diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts
index adee0a55a1..d112350ed7 100644
--- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts
+++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts
@@ -8,7 +8,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor';
import { URI } from 'vs/base/common/uri';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorInput, EditorOptions, IFileEditorInput, IEditorInput } from 'vs/workbench/common/editor';
-import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
+import { workbenchInstantiationService, TestStorageService, NullFileSystemProvider } from 'vs/workbench/test/workbenchTestServices';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService';
@@ -27,6 +27,8 @@ import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { CancellationToken } from 'vs/base/common/cancellation';
import { timeout } from 'vs/base/common/async';
import { toResource } from 'vs/base/test/common/utils';
+import { IFileService } from 'vs/platform/files/common/files';
+import { Disposable } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}} - Disable editor tests
/*
@@ -66,8 +68,17 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput {
this.gotDisposed = true;
}
}
-*/
-suite('Editor service', () => {/*
+
+class FileServiceProvider extends Disposable {
+ constructor(scheme: string, @IFileService fileService: IFileService) {
+ super();
+
+ this._register(fileService.registerProvider(scheme, new NullFileSystemProvider()));
+ }
+}
+
+*/suite('Editor service', () => {/*
+
function registerTestEditorInput(): void {
Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), new SyncDescriptor(TestEditorInput));
}
@@ -250,9 +261,23 @@ suite('Editor service', () => {/*
assert(input instanceof UntitledEditorInput);
// Untyped Input (untitled with file path)
- input = service.createInput({ filePath: '/some/path.txt', options: { selection: { startLineNumber: 1, startColumn: 1 } } });
+ input = service.createInput({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } });
assert(input instanceof UntitledEditorInput);
assert.ok((input as UntitledEditorInput).hasAssociatedFilePath);
+
+ // Untyped Input (untitled with untitled resource)
+ input = service.createInput({ resource: URI.parse('untitled://Untitled-1'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } });
+ assert(input instanceof UntitledEditorInput);
+ assert.ok(!(input as UntitledEditorInput).hasAssociatedFilePath);
+
+ // Untyped Input (untitled with custom resource)
+ const provider = instantiationService.createInstance(FileServiceProvider, 'untitled-custom');
+
+ input = service.createInput({ resource: URI.parse('untitled-custom://some/path'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } });
+ assert(input instanceof UntitledEditorInput);
+ assert.ok((input as UntitledEditorInput).hasAssociatedFilePath);
+
+ provider.dispose();
});
test('delegate', function (done) {
diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts
index cb9e94d193..2e3727a962 100644
--- a/src/vs/workbench/services/extensions/node/proxyResolver.ts
+++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts
@@ -481,7 +481,7 @@ function readWindowsCaCertificates() {
}
async function readMacCaCertificates() {
- const stdout = (await promisify(cp.execFile)('/usr/bin/security', ['find-certificate', '-a', '-p'], { encoding: 'utf8' })).stdout;
+ const stdout = (await promisify(cp.execFile)('/usr/bin/security', ['find-certificate', '-a', '-p'], { encoding: 'utf8', maxBuffer: 1024 * 1024 })).stdout;
const seen = {};
const certs = stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g)
.filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true));
diff --git a/src/vs/workbench/services/files/common/fileService.ts b/src/vs/workbench/services/files/common/fileService.ts
index 839705134b..15d3f7ed76 100644
--- a/src/vs/workbench/services/files/common/fileService.ts
+++ b/src/vs/workbench/services/files/common/fileService.ts
@@ -157,7 +157,7 @@ export class FileService extends Disposable implements IFileService {
}
// Bubble up any other error as is
- throw error;
+ throw this.ensureError(error);
}
}
@@ -206,7 +206,7 @@ export class FileService extends Disposable implements IFileService {
isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly),
mtime: stat.mtime,
size: stat.size,
- etag: etag(stat.mtime, stat.size)
+ etag: etag({ mtime: stat.mtime, size: stat.size })
};
// check to recurse for directories
@@ -306,7 +306,7 @@ export class FileService extends Disposable implements IFileService {
await this.doWriteUnbuffered(provider, resource, bufferOrReadable);
}
} catch (error) {
- throw new FileOperationError(localize('err.write', "Unable to write file ({0})", error.toString()), toFileOperationResult(error), options);
+ throw new FileOperationError(localize('err.write', "Unable to write file ({0})", this.ensureError(error).toString()), toFileOperationResult(error), options);
}
return this.resolve(resource, { resolveMetadata: true });
@@ -337,7 +337,10 @@ export class FileService extends Disposable implements IFileService {
// check for size is a weaker check because it can return a false negative if the file has changed
// but to the same length. This is a compromise we take to avoid having to produce checksums of
// the file content for comparison which would be much slower to compute.
- if (options && typeof options.mtime === 'number' && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && options.mtime < stat.mtime && options.etag !== etag(stat.size, options.mtime)) {
+ if (
+ options && typeof options.mtime === 'number' && typeof options.etag === 'string' &&
+ options.etag !== ETAG_DISABLED && options.mtime < stat.mtime && options.etag !== etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size })
+ ) {
throw new FileOperationError(localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE, options);
}
@@ -398,7 +401,7 @@ export class FileService extends Disposable implements IFileService {
value: fileStream
};
} catch (error) {
- throw new FileOperationError(localize('err.read', "Unable to read file ({0})", error.toString()), toFileOperationResult(error), options);
+ throw new FileOperationError(localize('err.read', "Unable to read file ({0})", this.ensureError(error).toString()), toFileOperationResult(error), options);
}
}
@@ -462,7 +465,7 @@ export class FileService extends Disposable implements IFileService {
stream.write(buffer.slice(0, lastChunkLength));
}
} catch (error) {
- throw error;
+ throw this.ensureError(error);
} finally {
await provider.close(handle);
}
@@ -492,8 +495,8 @@ export class FileService extends Disposable implements IFileService {
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", this.resourceForError(resource)), FileOperationResult.FILE_IS_DIRECTORY, options);
}
- // Return early if file not modified since
- if (options && options.etag === stat.etag) {
+ // Return early if file not modified since (unless disabled)
+ if (options && options.etag !== ETAG_DISABLED && options.etag === stat.etag) {
throw new FileOperationError(localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
}
@@ -863,7 +866,7 @@ export class FileService extends Disposable implements IFileService {
posInFile += chunk.byteLength;
}
} catch (error) {
- throw error;
+ throw this.ensureError(error);
} finally {
await provider.close(handle);
}
@@ -929,7 +932,7 @@ export class FileService extends Disposable implements IFileService {
}
} while (bytesRead > 0);
} catch (error) {
- throw error;
+ throw this.ensureError(error);
} finally {
await Promise.all([
typeof sourceHandle === 'number' ? sourceProvider.close(sourceHandle) : Promise.resolve(),
@@ -960,7 +963,7 @@ export class FileService extends Disposable implements IFileService {
const buffer = await sourceProvider.readFile(source);
await this.doWriteBuffer(targetProvider, targetHandle, VSBuffer.wrap(buffer), buffer.byteLength, 0, 0);
} catch (error) {
- throw error;
+ throw this.ensureError(error);
} finally {
await targetProvider.close(targetHandle);
}
@@ -991,6 +994,14 @@ export class FileService extends Disposable implements IFileService {
return true;
}
+ private ensureError(error?: Error): Error {
+ if (!error) {
+ return new Error(localize('unknownError', "Unknown Error")); // https://github.com/Microsoft/vscode/issues/72798
+ }
+
+ return error;
+ }
+
private throwIfTooLarge(totalBytesRead: number, options?: IReadFileOptions): boolean {
// Return early if file is too large to load
diff --git a/src/vs/workbench/services/files/test/node/diskFileService.test.ts b/src/vs/workbench/services/files/test/node/diskFileService.test.ts
index c63d7c39f8..29d19503eb 100644
--- a/src/vs/workbench/services/files/test/node/diskFileService.test.ts
+++ b/src/vs/workbench/services/files/test/node/diskFileService.test.ts
@@ -355,7 +355,7 @@ suite('Disk File Service', () => {
test('resolve - folder symbolic link', async () => {
if (isWindows) {
- return; // not happy
+ return; // not reliable on windows
}
const link = URI.file(join(testDir, 'deep-link'));
@@ -369,7 +369,7 @@ suite('Disk File Service', () => {
test('resolve - file symbolic link', async () => {
if (isWindows) {
- return; // not happy
+ return; // not reliable on windows
}
const link = URI.file(join(testDir, 'lorem.txt-linked'));
@@ -382,7 +382,7 @@ suite('Disk File Service', () => {
test('resolve - invalid symbolic link does not break', async () => {
if (isWindows) {
- return; // not happy
+ return; // not reliable on windows
}
const link = URI.file(join(testDir, 'foo'));
@@ -824,12 +824,24 @@ suite('Disk File Service', () => {
return testReadFile(URI.file(join(testDir, 'small.txt')));
});
+ test('readFile - small file - buffered / readonly', () => {
+ setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
+
+ return testReadFile(URI.file(join(testDir, 'small.txt')));
+ });
+
test('readFile - small file - unbuffered', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
return testReadFile(URI.file(join(testDir, 'small.txt')));
});
+ test('readFile - small file - unbuffered / readonly', async () => {
+ setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
+
+ return testReadFile(URI.file(join(testDir, 'small.txt')));
+ });
+
test('readFile - large file - buffered', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose);
@@ -1200,6 +1212,26 @@ suite('Disk File Service', () => {
assert.equal(readFileSync(resource.fsPath), newContent);
});
+ test('writeFile - buffered - readonly throws', async () => {
+ setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.Readonly);
+
+ const resource = URI.file(join(testDir, 'small.txt'));
+
+ const content = readFileSync(resource.fsPath);
+ assert.equal(content, 'Small File');
+
+ const newContent = 'Updates to the small file';
+
+ let error: Error;
+ try {
+ await service.writeFile(resource, VSBuffer.fromString(newContent));
+ } catch (err) {
+ error = err;
+ }
+
+ assert.ok(error!);
+ });
+
test('writeFile - unbuffered', async () => {
setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);
@@ -1228,6 +1260,26 @@ suite('Disk File Service', () => {
assert.equal(readFileSync(resource.fsPath), newContent);
});
+ test('writeFile - unbuffered - readonly throws', async () => {
+ setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly);
+
+ const resource = URI.file(join(testDir, 'small.txt'));
+
+ const content = readFileSync(resource.fsPath);
+ assert.equal(content, 'Small File');
+
+ const newContent = 'Updates to the small file';
+
+ let error: Error;
+ try {
+ await service.writeFile(resource, VSBuffer.fromString(newContent));
+ } catch (err) {
+ error = err;
+ }
+
+ assert.ok(error!);
+ });
+
test('writeFile (large file) - multiple parallel writes queue up', async () => {
const resource = URI.file(join(testDir, 'lorem.txt'));
@@ -1336,20 +1388,25 @@ suite('Disk File Service', () => {
assert.equal(readFileSync(resource.fsPath), newContent);
});
- test('writeFile (error when writing to file that has been updated meanwhile)', async () => {
+ test('writeFile - error when writing to file that has been updated meanwhile', async () => {
const resource = URI.file(join(testDir, 'small.txt'));
const stat = await service.resolve(resource);
- const content = readFileSync(resource.fsPath);
+ const content = readFileSync(resource.fsPath).toString();
assert.equal(content, 'Small File');
const newContent = 'Updates to the small file';
await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
+ const newContentLeadingToError = newContent + newContent;
+
+ const fakeMtime = 1000;
+ const fakeSize = 1000;
+
let error: FileOperationError | undefined = undefined;
try {
- await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: etag(0, 0), mtime: 0 });
+ await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToError), { etag: etag({ mtime: fakeMtime, size: fakeSize }), mtime: fakeMtime });
} catch (err) {
error = err;
}
@@ -1359,6 +1416,32 @@ suite('Disk File Service', () => {
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE);
});
+ test('writeFile - no error when writing to file where size is the same', async () => {
+ const resource = URI.file(join(testDir, 'small.txt'));
+
+ const stat = await service.resolve(resource);
+
+ const content = readFileSync(resource.fsPath).toString();
+ assert.equal(content, 'Small File');
+
+ const newContent = content; // same content
+ await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: stat.etag, mtime: stat.mtime });
+
+ const newContentLeadingToNoError = newContent; // writing the same content should be OK
+
+ const fakeMtime = 1000;
+ const actualSize = newContent.length;
+
+ let error: FileOperationError | undefined = undefined;
+ try {
+ await service.writeFile(resource, VSBuffer.fromString(newContentLeadingToNoError), { etag: etag({ mtime: fakeMtime, size: actualSize }), mtime: fakeMtime });
+ } catch (err) {
+ error = err;
+ }
+
+ assert.ok(!error);
+ });
+
test('watch - file', done => {
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
writeFileSync(toWatch.fsPath, 'Init');
@@ -1370,7 +1453,7 @@ suite('Disk File Service', () => {
test('watch - file symbolic link', async done => {
if (isWindows) {
- return done(); // not happy
+ return done(); // watch tests are flaky on other platforms
}
const toWatch = URI.file(join(testDir, 'lorem.txt-linked'));
@@ -1383,7 +1466,7 @@ suite('Disk File Service', () => {
test('watch - file - multiple writes', done => {
if (isWindows) {
- return done(); // not happy
+ return done(); // watch tests are flaky on other platforms
}
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
@@ -1446,6 +1529,10 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - change file', done => {
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
+ }
+
const watchDir = URI.file(join(testDir, 'watch3'));
mkdirSync(watchDir.fsPath);
@@ -1458,6 +1545,10 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - add file', done => {
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
+ }
+
const watchDir = URI.file(join(testDir, 'watch4'));
mkdirSync(watchDir.fsPath);
@@ -1469,6 +1560,10 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - delete file', done => {
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
+ }
+
const watchDir = URI.file(join(testDir, 'watch5'));
mkdirSync(watchDir.fsPath);
@@ -1481,6 +1576,10 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - add folder', done => {
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
+ }
+
const watchDir = URI.file(join(testDir, 'watch6'));
mkdirSync(watchDir.fsPath);
@@ -1492,8 +1591,8 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - delete folder', done => {
- if (isWindows) {
- return done(); // not happy
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
}
const watchDir = URI.file(join(testDir, 'watch7'));
@@ -1508,8 +1607,8 @@ suite('Disk File Service', () => {
});
test('watch - folder (non recursive) - symbolic link - change file', async done => {
- if (isWindows) {
- return done(); // not happy
+ if (!isLinux) {
+ return done(); // watch tests are flaky on other platforms
}
const watchDir = URI.file(join(testDir, 'deep-link'));
@@ -1525,7 +1624,7 @@ suite('Disk File Service', () => {
test('watch - folder (non recursive) - rename file', done => {
if (!isLinux) {
- return done(); // not happy
+ return done(); // watch tests are flaky on other platforms
}
const watchDir = URI.file(join(testDir, 'watch8'));
@@ -1543,7 +1642,7 @@ suite('Disk File Service', () => {
test('watch - folder (non recursive) - rename file (different case)', done => {
if (!isLinux) {
- return done(); // not happy
+ return done(); // watch tests are flaky on other platforms
}
const watchDir = URI.file(join(testDir, 'watch8'));
diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts
index 2eec056a09..ecb2429752 100644
--- a/src/vs/workbench/services/preferences/browser/preferencesService.ts
+++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts
@@ -137,7 +137,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
if (this.defaultSettingsRawResource.toString() === uri.toString()) {
- const defaultRawSettingsEditorModel = this.instantiationService.createInstance(DefaultRawSettingsEditorModel, this.getDefaultSettings(ConfigurationTarget.USER));
+ const defaultRawSettingsEditorModel = this.instantiationService.createInstance(DefaultRawSettingsEditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL));
const languageSelection = this.modeService.create('jsonc');
const model = this._register(this.modelService.createModel(defaultRawSettingsEditorModel.content, languageSelection, uri));
return Promise.resolve(model);
@@ -159,7 +159,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
if (this.userSettingsResource.toString() === uri.toString()) {
- return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri);
+ return this.createEditableSettingsEditorModel(ConfigurationTarget.USER_LOCAL, uri);
}
const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
@@ -209,8 +209,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic
jsonEditor;
return jsonEditor ?
- this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group) :
- this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group);
+ this.openOrSwitchSettings(ConfigurationTarget.USER_LOCAL, this.userSettingsResource, options, group) :
+ this.openOrSwitchSettings2(ConfigurationTarget.USER_LOCAL, undefined, options, group);
}
async openRemoteSettings(): Promise {
@@ -377,7 +377,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
public createSettings2EditorModel(): Settings2EditorModel {
- return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER));
+ return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL));
}
private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise {
@@ -419,7 +419,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private getConfigurationTargetFromSettingsResource(resource: URI): ConfigurationTarget {
if (this.userSettingsResource.toString() === resource.toString()) {
- return ConfigurationTarget.USER;
+ return ConfigurationTarget.USER_LOCAL;
}
const workspaceSettingsResource = this.workspaceSettingsResource;
@@ -432,11 +432,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return ConfigurationTarget.WORKSPACE_FOLDER;
}
- return ConfigurationTarget.USER;
+ return ConfigurationTarget.USER_LOCAL;
}
private getConfigurationTargetFromDefaultSettingsResource(uri: URI) {
- return this.isDefaultWorkspaceSettingsResource(uri) ? ConfigurationTarget.WORKSPACE : this.isDefaultFolderSettingsResource(uri) ? ConfigurationTarget.WORKSPACE_FOLDER : ConfigurationTarget.USER;
+ return this.isDefaultWorkspaceSettingsResource(uri) ?
+ ConfigurationTarget.WORKSPACE :
+ this.isDefaultFolderSettingsResource(uri) ?
+ ConfigurationTarget.WORKSPACE_FOLDER :
+ ConfigurationTarget.USER_LOCAL;
}
private isDefaultSettingsResource(uri: URI): boolean {
diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts
index 04ecdeee97..1e516d069a 100644
--- a/src/vs/workbench/services/progress/browser/progressService2.ts
+++ b/src/vs/workbench/services/progress/browser/progressService2.ts
@@ -309,7 +309,7 @@ export class ProgressService2 implements IProgressService2 {
disposables.push(attachDialogStyler(dialog, this._themeService));
dialog.show().then(() => {
- if (options.cancellable && typeof onDidCancel === 'function') {
+ if (typeof onDidCancel === 'function') {
onDidCancel();
}
diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
index 1d77cd9fc3..42bd2082c0 100644
--- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
+++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
@@ -31,6 +31,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { isEqual, isEqualOrParent, extname, basename } from 'vs/base/common/resources';
import { onUnexpectedError } from 'vs/base/common/errors';
+import { Schemas } from 'vs/base/common/network';
/**
* The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk.
@@ -300,7 +301,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Decide on etag
let etag: string | undefined;
if (forceReadFromDisk) {
- etag = undefined; // reset ETag if we enforce to read from disk
+ etag = ETAG_DISABLED; // disable ETag if we enforce to read from disk
} else if (this.lastResolvedDiskStat) {
etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching
}
@@ -826,10 +827,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
private getTelemetryData(reason: number | undefined): object {
const ext = extname(this.resource);
const fileName = basename(this.resource);
+ const path = this.resource.scheme === Schemas.file ? this.resource.fsPath : this.resource.path;
const telemetryData = {
- mimeType: guessMimeTypes(this.resource.fsPath).join(', '),
+ mimeType: guessMimeTypes(path).join(', '),
ext,
- path: hash(this.resource.fsPath),
+ path: hash(path),
reason
};
diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts
index 979e9868cb..445d7d01e6 100644
--- a/src/vs/workbench/services/textfile/common/textFileService.ts
+++ b/src/vs/workbench/services/textfile/common/textFileService.ts
@@ -871,13 +871,20 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
return false;
}
- // take over encoding and model value from source model
+ // take over encoding, mode and model value from source model
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
if (targetModel.textEditorModel) {
const snapshot = sourceModel.createSnapshot();
if (snapshot) {
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot));
}
+
+ if (sourceModel.textEditorModel) {
+ const language = sourceModel.textEditorModel.getLanguageIdentifier();
+ if (language.id > 1) {
+ targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text
+ }
+ }
}
// save model
diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
index 0e5639dad9..efda47b66a 100644
--- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
+++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
@@ -30,6 +30,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema';
import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
// implementation
// {{SQL CARBON EDIT}}
@@ -480,7 +481,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
this.doSetFileIconTheme(newIconTheme);
// remember theme data for a quick restore
- if (newIconTheme.isLoaded) {
+ if (newIconTheme.isLoaded && newIconTheme.location && !getRemoteAuthority(newIconTheme.location)) {
this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData(), StorageScope.GLOBAL);
}
diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts
index f9a00ac5be..752b31667a 100644
--- a/src/vs/workbench/test/workbenchTestServices.ts
+++ b/src/vs/workbench/test/workbenchTestServices.ts
@@ -1017,8 +1017,12 @@ export class TestFileService implements IFileService {
onDidChangeFileSystemProviderRegistrations = Event.None;
- registerProvider(_scheme: string, _provider: IFileSystemProvider) {
- return { dispose() { } };
+ private providers = new Map();
+
+ registerProvider(scheme: string, provider: IFileSystemProvider) {
+ this.providers.set(scheme, provider);
+
+ return toDisposable(() => this.providers.delete(scheme));
}
activateProvider(_scheme: string): Promise {
@@ -1026,7 +1030,7 @@ export class TestFileService implements IFileService {
}
canHandleResource(resource: URI): boolean {
- return resource.scheme === 'file';
+ return resource.scheme === 'file' || this.providers.has(resource.scheme);
}
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean { return false; }
diff --git a/yarn.lock b/yarn.lock
index 9690082eb7..243d3e378d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9257,10 +9257,10 @@ typescript-tslint-plugin@^0.0.7:
minimatch "^3.0.4"
vscode-languageserver "^5.1.0"
-typescript@3.4.1:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
- integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
+typescript@3.4.5:
+ version "3.4.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
+ integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
typescript@^2.6.2:
version "2.6.2"