From 269837fbf400b92c09b3158edf5709bd73bbd663 Mon Sep 17 00:00:00 2001 From: "mgquan@myseneca.ca" Date: Wed, 26 Dec 2018 14:38:25 -0500 Subject: [PATCH 01/89] fixed 59961 --- src/vs/editor/contrib/multicursor/multicursor.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index da531f7a83d..69dce64d28b 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -608,6 +608,17 @@ export class MultiCursorSelectionController extends Disposable implements IEdito matches = this._session.selectAll(); } + if (findState.searchScope) { + const state = findState.searchScope; + let inSelection: FindMatch[] | null = []; + for (let i = 0; i < matches.length; i++) { + if (matches[i].range.endLineNumber <= state.endLineNumber && matches[i].range.startLineNumber >= state.startLineNumber) { + inSelection.push(matches[i]); + } + } + matches = inSelection; + } + if (matches.length > 0) { const editorSelection = this._editor.getSelection(); // Have the primary cursor remain the one where the action was invoked From 89336a9155dd8121d2fb1652c395ffefe6c2274d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alonso=20Abad?= Date: Fri, 29 Mar 2019 13:55:06 +0100 Subject: [PATCH 02/89] Perl5 support to fold POD blocks POD blocks (as defined with the =pod and =cut tags) should be foldable. This documentation format is excessively verbose and clutters the file when other sections are folded (especially after a "fold all" operation). --- extensions/perl/perl.language-configuration.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/perl/perl.language-configuration.json b/extensions/perl/perl.language-configuration.json index 01b6a8a2823..6cf6295b798 100644 --- a/extensions/perl/perl.language-configuration.json +++ b/extensions/perl/perl.language-configuration.json @@ -22,5 +22,11 @@ ["\"", "\""], ["'", "'"], ["`", "`"] - ] + ], + "folding": { + "markers": { + "start": "^=pod\\s*$", + "end": "^=cut\\s*$" + } + } } \ No newline at end of file From b42bc6fcd224aa1206fbee75947d0a5f6aa5f24e Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Mon, 7 Oct 2019 18:11:52 +0200 Subject: [PATCH 03/89] Update modesContentHover.ts --- src/vs/editor/contrib/hover/modesContentHover.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 06877b671d5..8a690a10b15 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -353,7 +353,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { containColorPicker = true; const { red, green, blue, alpha } = msg.color; - const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha); + const rgba = new RGBA(Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255), alpha); const color = new Color(rgba); if (!this._editor.hasModel()) { From 3c51e4bce5c10827bcef57e6f4e5e84bb0f38371 Mon Sep 17 00:00:00 2001 From: Jeremy Shore Date: Fri, 18 Oct 2019 00:54:50 -0500 Subject: [PATCH 04/89] Set astrisk as a delimiter for URIs --- src/vs/editor/common/modes/linkComputer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 6673c04f64a..0bb9c7684d6 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -264,6 +264,9 @@ export class LinkComputer { case CharCode.BackTick: chClass = (linkBeginChCode === CharCode.SingleQuote || linkBeginChCode === CharCode.DoubleQuote) ? CharacterClass.None : CharacterClass.ForceTermination; break; + case CharCode.Asterisk: + chClass = CharacterClass.ForceTermination; + break; default: chClass = classifier.get(chCode); } From 3375a209855d86f0fda759bad29eb227cbc0057a Mon Sep 17 00:00:00 2001 From: Jeremy Shore Date: Fri, 18 Oct 2019 19:51:18 -0500 Subject: [PATCH 05/89] Fix tests for linkComputer --- src/vs/editor/test/common/modes/linkComputer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts index b4f3b76f688..52db6612ce0 100644 --- a/src/vs/editor/test/common/modes/linkComputer.test.ts +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -185,7 +185,7 @@ suite('Editor Modes - Link Computer', () => { ); assertLink( 'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', - ' http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items ' + ' http://* ' ); }); From 359c17a6666c4853e792d65fd3ce1f064ae1d6d6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 22 Oct 2019 11:31:08 +0200 Subject: [PATCH 06/89] custom encode/decode logic --- src/vs/base/common/uri.ts | 309 +++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 156 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 7df9153b440..d30d53eb7b4 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -5,6 +5,7 @@ import { isWindows } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; +import { isHighSurrogate, isLowSurrogate } from 'vs/base/common/strings'; const _schemePattern = /^\w[\w\d+.-]*$/; const _singleSlashStart = /^\//; @@ -265,10 +266,10 @@ export class URI implements UriComponents { } return new _URI( match[2] || _empty, - decodeURIComponent(match[4] || _empty), - decodeURIComponent(match[5] || _empty), - decodeURIComponent(match[7] || _empty), - decodeURIComponent(match[9] || _empty) + percentDecode(match[4] || _empty), + percentDecode(match[5] || _empty), + percentDecode(match[7] || _empty), + percentDecode(match[9] || _empty) ); } @@ -344,7 +345,7 @@ export class URI implements UriComponents { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding: boolean = false): string { - return _asFormatted(this, skipEncoding); + return _toString(skipEncoding, this.scheme, this.authority, this.path, this.query, this.fragment); } toJSON(): UriComponents { @@ -400,15 +401,14 @@ class _URI extends URI { } toString(skipEncoding: boolean = false): string { - if (!skipEncoding) { - if (!this._formatted) { - this._formatted = _asFormatted(this, false); - } - return this._formatted; - } else { + if (skipEncoding) { // we don't cache that - return _asFormatted(this, true); + return _toString(true, this.scheme, this.authority, this.path, this.query, this.fragment); } + if (!this._formatted) { + this._formatted = _toString(false, this.scheme, this.authority, this.path, this.query, this.fragment); + } + return this._formatted; } toJSON(): UriComponents { @@ -443,110 +443,6 @@ class _URI extends URI { } } -// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2 -const encodeTable: { [ch: number]: string; } = { - [CharCode.Colon]: '%3A', // gen-delims - [CharCode.Slash]: '%2F', - [CharCode.QuestionMark]: '%3F', - [CharCode.Hash]: '%23', - [CharCode.OpenSquareBracket]: '%5B', - [CharCode.CloseSquareBracket]: '%5D', - [CharCode.AtSign]: '%40', - - [CharCode.ExclamationMark]: '%21', // sub-delims - [CharCode.DollarSign]: '%24', - [CharCode.Ampersand]: '%26', - [CharCode.SingleQuote]: '%27', - [CharCode.OpenParen]: '%28', - [CharCode.CloseParen]: '%29', - [CharCode.Asterisk]: '%2A', - [CharCode.Plus]: '%2B', - [CharCode.Comma]: '%2C', - [CharCode.Semicolon]: '%3B', - [CharCode.Equals]: '%3D', - - [CharCode.Space]: '%20', -}; - -function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): string { - let res: string | undefined = undefined; - let nativeEncodePos = -1; - - for (let pos = 0; pos < uriComponent.length; pos++) { - const code = uriComponent.charCodeAt(pos); - - // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3 - if ( - (code >= CharCode.a && code <= CharCode.z) - || (code >= CharCode.A && code <= CharCode.Z) - || (code >= CharCode.Digit0 && code <= CharCode.Digit9) - || code === CharCode.Dash - || code === CharCode.Period - || code === CharCode.Underline - || code === CharCode.Tilde - || (allowSlash && code === CharCode.Slash) - ) { - // check if we are delaying native encode - if (nativeEncodePos !== -1) { - res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); - nativeEncodePos = -1; - } - // check if we write into a new string (by default we try to return the param) - if (res !== undefined) { - res += uriComponent.charAt(pos); - } - - } else { - // encoding needed, we need to allocate a new string - if (res === undefined) { - res = uriComponent.substr(0, pos); - } - - // check with default table first - const escaped = encodeTable[code]; - if (escaped !== undefined) { - - // check if we are delaying native encode - if (nativeEncodePos !== -1) { - res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); - nativeEncodePos = -1; - } - - // append escaped variant to result - res += escaped; - - } else if (nativeEncodePos === -1) { - // use native encode only when needed - nativeEncodePos = pos; - } - } - } - - if (nativeEncodePos !== -1) { - res += encodeURIComponent(uriComponent.substring(nativeEncodePos)); - } - - return res !== undefined ? res : uriComponent; -} - -function encodeURIComponentMinimal(path: string): string { - let res: string | undefined = undefined; - for (let pos = 0; pos < path.length; pos++) { - const code = path.charCodeAt(pos); - if (code === CharCode.Hash || code === CharCode.QuestionMark) { - if (res === undefined) { - res = path.substr(0, pos); - } - res += encodeTable[code]; - } else { - if (res !== undefined) { - res += path[pos]; - } - } - } - return res !== undefined ? res : path; -} - /** * Compute `fsPath` for the given uri */ @@ -573,17 +469,122 @@ function _makeFsPath(uri: URI): string { return value; } + +//#region ---- decode + +function decodeURIComponentGraceful(str: string): string { + try { + return decodeURIComponent(str); + } catch { + if (str.length > 3) { + return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3)); + } else { + return str; + } + } +} + +const _hex2 = /(%[0-9A-Za-z][0-9A-Za-z])+/g; +function percentDecode(str: string): string { + if (!str.match(_hex2)) { + return str; + } + return str.replace(_hex2, (match) => decodeURIComponentGraceful(match)); +} + +//#endregion + +//#region ---- encode + +// https://url.spec.whatwg.org/#percent-encoded-bytes +// "The C0 control percent-encode set are the C0 controls and all code points greater than U+007E (~)." + +function isC0ControlPercentEncodeSet(code: number): boolean { + return code <= 0x1F || code > 0x7E; +} +// "The fragment percent-encode set is the C0 control percent-encode set and U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`)." +function isFragmentPercentEncodeSet(code: number): boolean { + return isC0ControlPercentEncodeSet(code) + || code === 0x20 || code === 0x22 || code === 0x3C || code === 0x3E || code === 0x60; +} +// "The path percent-encode set is the fragment percent-encode set and U+0023 (#), U+003F (?), U+007B ({), and U+007D (})." +function isPathPercentEncodeSet(code: number): boolean { + return isFragmentPercentEncodeSet(code) + || code === 0x23 || code === 0x3F || code === 0x7B || code === 0x7D; +} +// "The userinfo percent-encode set is the path percent-encode set and U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@), U+005B ([), U+005C (\), U+005D (]), U+005E (^), and U+007C (|)." +function isUserInfoPercentEncodeSet(code: number): boolean { + return isPathPercentEncodeSet(code) + || code === 0x2F || code === 0x3A || code === 0x3B || code === 0x3D || code === 0x40 + || code === 0x5B || code === 0x5C || code === 0x5D || code === 0x5E || code === 0x7C; +} + +// https://url.spec.whatwg.org/#query-state +function isQueryPrecentEncodeSet(code: number): boolean { + return code < 0x21 || code > 0x7E + || code === 0x22 || code === 0x23 || code === 0x3C || code === 0x3E + || code === 0x27; // <- todo@joh https://url.spec.whatwg.org/#is-special +} + +function isLowerAsciiHex(code: number): boolean { + return code >= CharCode.Digit0 && code <= CharCode.Digit9 + || code >= CharCode.a && code <= CharCode.z; +} + +function percentEncode(str: string, mustEncode: (code: number) => boolean): string { + let lazyOutStr: string | undefined; + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i); + + // invoke encodeURIComponent when needed + if (mustEncode(code)) { + if (!lazyOutStr) { + lazyOutStr = str.substr(0, i); + } + // + if (isHighSurrogate(code)) { + if (i + 1 < str.length && isLowSurrogate(str.charCodeAt(i + 1))) { + lazyOutStr += encodeURIComponent(str.substr(i, 2)); + i += 1; + } else { + // broken surrogate pair + lazyOutStr += str.charAt(i); + } + } else { + // todo@joh PERF, use lookup table + lazyOutStr += encodeURIComponent(str.charAt(i)); + } + continue; + } + + // normalize percent encoded sequences to upper case + // todo@joh also changes invalid sequences + if (code === CharCode.PercentSign + && i + 2 < str.length + && (isLowerAsciiHex(str.charCodeAt(i + 1)) || isLowerAsciiHex(str.charCodeAt(i + 2))) + ) { + if (!lazyOutStr) { + lazyOutStr = str.substr(0, i); + } + lazyOutStr += '%' + str.substr(i + 1, 2).toUpperCase(); + i += 2; + continue; + } + + // once started, continue to build up lazy output + if (lazyOutStr) { + lazyOutStr += str.charAt(i); + } + } + return lazyOutStr || str; +} + /** * Create the external version of a uri */ -function _asFormatted(uri: URI, skipEncoding: boolean): string { - - const encoder = !skipEncoding - ? encodeURIComponentFast - : encodeURIComponentMinimal; +function _toString(minimal: boolean, scheme: string, authority: string, path: string, query: string, fragment: string): string { let res = ''; - let { scheme, authority, path, query, fragment } = uri; if (scheme) { res += scheme; res += ':'; @@ -593,55 +594,51 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string { res += _slash; } if (authority) { - let idx = authority.indexOf('@'); - if (idx !== -1) { - // @ - const userinfo = authority.substr(0, idx); - authority = authority.substr(idx + 1); - idx = userinfo.indexOf(':'); - if (idx === -1) { - res += encoder(userinfo, false); - } else { - // :@ - res += encoder(userinfo.substr(0, idx), false); + const idxUserInfo = authority.indexOf('@'); + if (idxUserInfo !== -1) { + // + const userInfo = authority.substr(0, idxUserInfo); + const idxPasswordOrToken = userInfo.indexOf(':'); + if (idxPasswordOrToken !== -1) { + res += percentEncode(userInfo.substr(0, idxPasswordOrToken), isUserInfoPercentEncodeSet); res += ':'; - res += encoder(userinfo.substr(idx + 1), false); + res += percentEncode(userInfo.substr(idxPasswordOrToken + 1), isUserInfoPercentEncodeSet); + } else { + res += percentEncode(userInfo, isUserInfoPercentEncodeSet); } res += '@'; } - authority = authority.toLowerCase(); - idx = authority.indexOf(':'); - if (idx === -1) { - res += encoder(authority, false); - } else { - // : - res += encoder(authority.substr(0, idx), false); - res += authority.substr(idx); + authority = authority.substr(idxUserInfo + 1).toLowerCase(); + const idxPort = authority.indexOf(':'); + if (idxPort !== -1) { + // : + res += percentEncode(authority.substr(0, idxPort), isC0ControlPercentEncodeSet); + res += ':'; } + res += percentEncode(authority.substr(idxPort + 1), isC0ControlPercentEncodeSet); } if (path) { - // lower-case windows drive letters in /C:/fff or C:/fff - if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) { - const code = path.charCodeAt(1); - if (code >= CharCode.A && code <= CharCode.Z) { - path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 - } - } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { - const code = path.charCodeAt(0); - if (code >= CharCode.A && code <= CharCode.Z) { - path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3 + // encode the path + let pathEncoded = percentEncode(path, isPathPercentEncodeSet); + + // lower-case windows drive letters in /C:/fff or C:/fff and escape `:` + if (!minimal) { + let match = /(\/?[a-zA-Z]):/.exec(pathEncoded); // <- todo@joh make fast! + if (match) { + pathEncoded = match[1].toLowerCase() + '%3A' + pathEncoded.substr(match[0].length); } } - // encode the rest of the path - res += encoder(path, true); + res += pathEncoded; } if (query) { res += '?'; - res += encoder(query, false); + res += percentEncode(query, isQueryPrecentEncodeSet); } if (fragment) { res += '#'; - res += !skipEncoding ? encodeURIComponentFast(fragment, false) : fragment; + res += percentEncode(fragment, isFragmentPercentEncodeSet); } return res; } + +//#endregion From 3e0ded9c8d5062b105b2310aa699650001c521c9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 22 Oct 2019 12:20:26 +0200 Subject: [PATCH 07/89] keep input (for toString) when using URI.parse, URI.file uses now URI.parse Add tests that check our logic against Nodejs's pathToFileURL --- src/vs/base/common/uri.ts | 42 ++++++++++++++---- src/vs/base/test/{common => node}/uri.test.ts | 44 +++++++++++++------ 2 files changed, 65 insertions(+), 21 deletions(-) rename src/vs/base/test/{common => node}/uri.test.ts (94%) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index d30d53eb7b4..d2e21e84f96 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -74,8 +74,17 @@ function _referenceResolution(scheme: string, path: string): string { const _empty = ''; const _slash = '/'; + const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; +const enum MatchIndex { + scheme = 2, + authority = 4, + path = 5, + query = 7, + fragment = 9 +} + /** * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. * This class is a simple parser which creates the basic component parts @@ -262,15 +271,24 @@ export class URI implements UriComponents { static parse(value: string): URI { const match = _regexp.exec(value); if (!match) { - return new _URI(_empty, _empty, _empty, _empty, _empty); + throw new Error(`[UriError]: Invalid input: ${value}`); } - return new _URI( - match[2] || _empty, - percentDecode(match[4] || _empty), - percentDecode(match[5] || _empty), - percentDecode(match[7] || _empty), - percentDecode(match[9] || _empty) + + const scheme = _schemeFix(match[MatchIndex.scheme]) || _empty; + const authority = match[MatchIndex.authority] || _empty; + const path = _referenceResolution(scheme, match[MatchIndex.path] || _empty); + const query = match[MatchIndex.query] || _empty; + const fragment = match[MatchIndex.fragment] || _empty; + + const result = new _URI( + scheme, + percentDecode(authority), + percentDecode(path), + percentDecode(query), + percentDecode(fragment), ); + result._formatted = _toString(false, scheme, authority, path, query, fragment); + return result; } /** @@ -318,7 +336,15 @@ export class URI implements UriComponents { } } - return new _URI('file', authority, path, _empty, _empty); + if (path.charAt(0) !== _slash) { + path = _slash + path; + } + + // escape some vital characters + authority = authority.replace(/%/g, '%25'); + path = path.replace(/%/g, '%25'); + + return URI.parse('file://' + authority + path); } static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string; }): URI { diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/node/uri.test.ts similarity index 94% rename from src/vs/base/test/common/uri.test.ts rename to src/vs/base/test/node/uri.test.ts index 3255575ae57..31635f60a67 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; - +import { pathToFileURL } from 'url'; suite('URI', () => { test('file#toString', () => { @@ -427,21 +427,24 @@ suite('URI', () => { }); test('Unable to open \'%A0.txt\': URI malformed #76506', function () { - - let uri = URI.file('/foo/%A0.txt'); - let uri2 = URI.parse(uri.toString()); - assert.equal(uri.scheme, uri2.scheme); - assert.equal(uri.path, uri2.path); - - uri = URI.file('/foo/%2e.txt'); - uri2 = URI.parse(uri.toString()); - assert.equal(uri.scheme, uri2.scheme); - assert.equal(uri.path, uri2.path); + let uriFromPath = URI.file('/foo/%A0.txt'); + let uriFromStr = URI.parse(uriFromPath.toString()); + assert.equal(uriFromPath.scheme, uriFromStr.scheme); + assert.equal(uriFromPath.path, uriFromStr.path); + assert.equal(uriFromPath.toString(), 'file:///foo/%25A0.txt'); + assert.equal(uriFromStr.toString(), 'file:///foo/%25A0.txt'); }); + test('Valid percent-encoded character in filename', function () { + let uriFromPath = URI.file('/foo/%2e.txt'); + let uriFromStr = URI.parse(uriFromPath.toString()); + assert.equal(uriFromPath.scheme, uriFromStr.scheme); + assert.equal(uriFromPath.path, uriFromStr.path); + assert.equal(uriFromPath.toString(), 'file:///foo/%252e.txt'); + assert.equal(uriFromStr.toString(), 'file:///foo/%252e.txt'); + }); test('Links in markdown are broken if url contains encoded parameters #79474', function () { - this.skip(); let strIn = 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'; let uri1 = URI.parse(strIn); let strOut = uri1.toString(); @@ -456,7 +459,6 @@ suite('URI', () => { }); test('Uri#parse can break path-component #45515', function () { - this.skip(); let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; let uri1 = URI.parse(strIn); let strOut = uri1.toString(); @@ -499,4 +501,20 @@ suite('URI', () => { // } // console.profileEnd(); }); + + function assertFileUri(path: string): void { + // check that our uri aligns with nodejs + const actual = URI.file(path).toString(); + const expected = pathToFileURL(path).href; + assert.equal(actual, expected); + } + + test('URI.file vs pathToFileURL', function () { + assertFileUri('/foo/bar'); + assertFileUri('/foo/%2e.txt'); // %2e -> . + assertFileUri('/foo/%A0.txt'); // %A0 -> invalid + assertFileUri('/foo/ü.txt'); + assertFileUri('/foo/ß.txt'); + // assertFileUri('foo'); nodejs resolves the path first + }); }); From 40e3b803d721a377147663fb089b5696cb0bc3b6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 22 Oct 2019 12:52:01 +0200 Subject: [PATCH 08/89] tweak test for less encoding, restore URI.toString(true) behaviour, escape backslash in non-windows cases --- src/vs/base/common/uri.ts | 50 ++++++++++++++++++++----------- src/vs/base/test/node/uri.test.ts | 25 +++++++++------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index d2e21e84f96..ff4fd6c86ff 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -287,7 +287,7 @@ export class URI implements UriComponents { percentDecode(query), percentDecode(fragment), ); - result._formatted = _toString(false, scheme, authority, path, query, fragment); + result._formatted = _toString(normalEncode, scheme, authority, path, query, fragment); return result; } @@ -344,6 +344,10 @@ export class URI implements UriComponents { authority = authority.replace(/%/g, '%25'); path = path.replace(/%/g, '%25'); + if (!isWindows) { + path = path.replace(/\\/g, '%5C'); + } + return URI.parse('file://' + authority + path); } @@ -371,7 +375,7 @@ export class URI implements UriComponents { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding: boolean = false): string { - return _toString(skipEncoding, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(skipEncoding ? minialEncode : normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); } toJSON(): UriComponents { @@ -429,10 +433,10 @@ class _URI extends URI { toString(skipEncoding: boolean = false): string { if (skipEncoding) { // we don't cache that - return _toString(true, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(minialEncode, this.scheme, this.authority, this.path, this.query, this.fragment); } if (!this._formatted) { - this._formatted = _toString(false, this.scheme, this.authority, this.path, this.query, this.fragment); + this._formatted = _toString(normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); } return this._formatted; } @@ -552,6 +556,11 @@ function isQueryPrecentEncodeSet(code: number): boolean { || code === 0x27; // <- todo@joh https://url.spec.whatwg.org/#is-special } +// this is non-standard and uses for `URI.toString(true)` +function isHashOrQuestionMark(code: number): boolean { + return code === CharCode.Hash || code === CharCode.QuestionMark; +} + function isLowerAsciiHex(code: number): boolean { return code >= CharCode.Digit0 && code <= CharCode.Digit9 || code >= CharCode.a && code <= CharCode.z; @@ -605,10 +614,16 @@ function percentEncode(str: string, mustEncode: (code: number) => boolean): stri return lazyOutStr || str; } +const enum EncodePart { + user, authority, path, query, fragment +} +const normalEncode: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; +const minialEncode: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; + /** * Create the external version of a uri */ -function _toString(minimal: boolean, scheme: string, authority: string, path: string, query: string, fragment: string): string { +function _toString(encoder: { (code: number): boolean }[], scheme: string, authority: string, path: string, query: string, fragment: string): string { let res = ''; if (scheme) { @@ -626,11 +641,11 @@ function _toString(minimal: boolean, scheme: string, authority: string, path: st const userInfo = authority.substr(0, idxUserInfo); const idxPasswordOrToken = userInfo.indexOf(':'); if (idxPasswordOrToken !== -1) { - res += percentEncode(userInfo.substr(0, idxPasswordOrToken), isUserInfoPercentEncodeSet); + res += percentEncode(userInfo.substr(0, idxPasswordOrToken), encoder[EncodePart.user]); res += ':'; - res += percentEncode(userInfo.substr(idxPasswordOrToken + 1), isUserInfoPercentEncodeSet); + res += percentEncode(userInfo.substr(idxPasswordOrToken + 1), encoder[EncodePart.user]); } else { - res += percentEncode(userInfo, isUserInfoPercentEncodeSet); + res += percentEncode(userInfo, encoder[EncodePart.user]); } res += '@'; } @@ -638,31 +653,30 @@ function _toString(minimal: boolean, scheme: string, authority: string, path: st const idxPort = authority.indexOf(':'); if (idxPort !== -1) { // : - res += percentEncode(authority.substr(0, idxPort), isC0ControlPercentEncodeSet); + res += percentEncode(authority.substr(0, idxPort), encoder[EncodePart.authority]); res += ':'; } - res += percentEncode(authority.substr(idxPort + 1), isC0ControlPercentEncodeSet); + res += percentEncode(authority.substr(idxPort + 1), encoder[EncodePart.authority]); } if (path) { // encode the path - let pathEncoded = percentEncode(path, isPathPercentEncodeSet); + let pathEncoded = percentEncode(path, encoder[EncodePart.path]); // lower-case windows drive letters in /C:/fff or C:/fff and escape `:` - if (!minimal) { - let match = /(\/?[a-zA-Z]):/.exec(pathEncoded); // <- todo@joh make fast! - if (match) { - pathEncoded = match[1].toLowerCase() + '%3A' + pathEncoded.substr(match[0].length); - } + + let match = /(\/?[a-zA-Z]):/.exec(pathEncoded); // <- todo@joh make fast! + if (match) { + pathEncoded = match[1].toLowerCase() + '%3A' + pathEncoded.substr(match[0].length); } res += pathEncoded; } if (query) { res += '?'; - res += percentEncode(query, isQueryPrecentEncodeSet); + res += percentEncode(query, encoder[EncodePart.query]); } if (fragment) { res += '#'; - res += percentEncode(fragment, isFragmentPercentEncodeSet); + res += percentEncode(fragment, encoder[EncodePart.fragment]); } return res; } diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 31635f60a67..6d948736b0c 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -63,8 +63,8 @@ suite('URI', () => { assert.equal(URI.from({ scheme: 'http', authority: '', path: 'my/path' }).toString(), 'http:/my/path'); assert.equal(URI.from({ scheme: 'http', authority: '', path: '/my/path' }).toString(), 'http:/my/path'); //http://a-test-site.com/#test=true - assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test%3Dtrue'); - assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(), 'http://a-test-site.com/#test%3Dtrue'); + assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test=true'); + assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(), 'http://a-test-site.com/#test=true'); }); test('http#toString, encode=FALSE', () => { @@ -102,12 +102,12 @@ suite('URI', () => { test('with, changes', () => { assert.equal(URI.parse('before:some/file/path').with({ scheme: 'after' }).toString(), 'after:some/file/path'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t=1234'); }); test('with, remove components #8465', () => { @@ -327,7 +327,7 @@ suite('URI', () => { test('URI#toString, escape all the bits', () => { const value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); - assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20%2B%20Other%20Th%C3%AEng%C3%9F/model.js'); + assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20+%20Other%20Th%C3%AEng%C3%9F/model.js'); }); test('URI#toString, don\'t encode port', () => { @@ -376,7 +376,7 @@ suite('URI', () => { let uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008'); assert.equal(uri.query, 'LinkId=518008'); assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008'); - assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008'); + assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId=518008'); let uri2 = URI.parse(uri.toString()); assert.equal(uri2.query, 'LinkId=518008'); @@ -385,7 +385,7 @@ suite('URI', () => { uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü'); assert.equal(uri.query, 'LinkId=518008&foö&ké¥=üü'); assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü'); - assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008%26fo%C3%B6%26k%C3%A9%C2%A5%3D%C3%BC%C3%BC'); + assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId=518008&fo%C3%B6&k%C3%A9%C2%A5=%C3%BC%C3%BC'); uri2 = URI.parse(uri.toString()); assert.equal(uri2.query, 'LinkId=518008&foö&ké¥=üü'); @@ -516,5 +516,8 @@ suite('URI', () => { assertFileUri('/foo/ü.txt'); assertFileUri('/foo/ß.txt'); // assertFileUri('foo'); nodejs resolves the path first + if (!isWindows) { + assertFileUri('/c\\win\\path'); + } }); }); From 380aa3c05572bb2faf729e1ddcb1a1a7806b0319 Mon Sep 17 00:00:00 2001 From: frobinsonj Date: Tue, 22 Oct 2019 18:38:57 +0100 Subject: [PATCH 09/89] Control find widget using transforms & Removed reduntant multipleline CSS class --- src/vs/editor/contrib/find/findWidget.css | 25 ++++------------------- src/vs/editor/contrib/find/findWidget.ts | 6 ------ 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 23cf08a1ca2..46c8e404cae 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -32,13 +32,13 @@ .monaco-editor .find-widget { position: absolute; z-index: 10; - top: -44px; height: 33px; overflow: hidden; line-height: 19px; - transition: top 200ms linear; + transition: transform 200ms linear; padding: 0 4px; box-sizing: border-box; + transform: translateY(Calc(-100% - 10px)); /* shadow (10px) */ } .monaco-editor .find-widget.hiddenEditor { @@ -46,30 +46,13 @@ } /* Find widget when replace is toggled on */ -.monaco-editor .find-widget.replaceToggled { - top: -74px; /* find input height + replace input height + shadow (10px) */ -} .monaco-editor .find-widget.replaceToggled > .replace-part { display: flex; display: -webkit-flex; } -.monaco-editor .find-widget.visible, -.monaco-editor .find-widget.replaceToggled.visible { - top: 0; -} - -/* Multiple line find widget */ - -.monaco-editor .find-widget.multipleline { - top: unset; - bottom: 10px; -} - -.monaco-editor .find-widget.multipleline.visible, -.monaco-editor .find-widget.multipleline.replaceToggled.visible { - top: 0px; - bottom: unset; +.monaco-editor .find-widget.visible { + transform: translateY(0); } .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index cb93002ad42..308f4262a17 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -290,12 +290,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _onStateChanged(e: FindReplaceStateChangedEvent): void { if (e.searchString) { - if (this._state.searchString.indexOf('\n') >= 0) { - dom.addClass(this._domNode, 'multipleline'); - } else { - dom.removeClass(this._domNode, 'multipleline'); - } - try { this._ignoreChangeEvent = true; this._findInput.setValue(this._state.searchString); From 540cffbee9d387fe018bb4463036057de223a3ca Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 22 Oct 2019 11:19:12 -0700 Subject: [PATCH 10/89] Remove duplicate uncategorized terminal commands Fixes #82901 --- .../terminal/browser/terminal.contribution.ts | 20 +++++++++---------- .../terminal/browser/terminalActions.ts | 5 ----- .../terminal/browser/terminalInstance.ts | 10 +++++----- .../contrib/terminal/common/terminal.ts | 5 ----- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index f5e13757b0c..0f006df9f5f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -518,28 +518,28 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_R, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using regex'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID_TERMINAL_FOCUS, ToggleRegexCommand.LABEL, { +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using regex', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_R, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using regex', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_W, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using whole word'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID_TERMINAL_FOCUS, ToggleWholeWordCommand.LABEL, { +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using whole word', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_W, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using whole word', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_C, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using case sensitive'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID_TERMINAL_FOCUS, ToggleCaseSensitiveCommand.LABEL, { +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using case sensitive', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_C, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using case sensitive', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID_TERMINAL_FOCUS, FindNext.LABEL, { +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); @@ -547,8 +547,8 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe primary: KeyCode.F3, secondary: [KeyMod.Shift | KeyCode.Enter], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); @@ -556,7 +556,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi primary: KeyMod.Shift | KeyCode.F3, secondary: [KeyCode.Enter], mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] }, -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous', category); (new SendSequenceTerminalCommand({ id: SendSequenceTerminalCommand.ID, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 5c6c751e720..3b2f3e7b9a9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1328,7 +1328,6 @@ abstract class ToggleFindOptionCommand extends Action { export class ToggleRegexCommand extends ToggleFindOptionCommand { public static readonly ID = TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX; - public static readonly ID_TERMINAL_FOCUS = TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX_TERMINAL_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.toggleFindRegex', "Toggle find using regex"); protected runInner(state: FindReplaceState): void { @@ -1338,7 +1337,6 @@ export class ToggleRegexCommand extends ToggleFindOptionCommand { export class ToggleWholeWordCommand extends ToggleFindOptionCommand { public static readonly ID = TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD; - public static readonly ID_TERMINAL_FOCUS = TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.toggleFindWholeWord', "Toggle find using whole word"); protected runInner(state: FindReplaceState): void { @@ -1348,7 +1346,6 @@ export class ToggleWholeWordCommand extends ToggleFindOptionCommand { export class ToggleCaseSensitiveCommand extends ToggleFindOptionCommand { public static readonly ID = TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE; - public static readonly ID_TERMINAL_FOCUS = TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.toggleFindCaseSensitive', "Toggle find using case sensitive"); protected runInner(state: FindReplaceState): void { @@ -1358,7 +1355,6 @@ export class ToggleCaseSensitiveCommand extends ToggleFindOptionCommand { export class FindNext extends Action { public static readonly ID = TERMINAL_COMMAND_ID.FIND_NEXT; - public static readonly ID_TERMINAL_FOCUS = TERMINAL_COMMAND_ID.FIND_NEXT_TERMINAL_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.findNext', "Find next"); constructor( @@ -1376,7 +1372,6 @@ export class FindNext extends Action { export class FindPrevious extends Action { public static readonly ID = TERMINAL_COMMAND_ID.FIND_PREVIOUS; - public static readonly ID_TERMINAL_FOCUS = TERMINAL_COMMAND_ID.FIND_PREVIOUS_TERMINAL_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.findPrevious', "Find previous"); constructor( diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 88a2126a572..46bd862a935 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -54,11 +54,11 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT, TERMINAL_COMMAND_ID.FIND_WIDGET_FOCUS, TERMINAL_COMMAND_ID.FIND_WIDGET_HIDE, - TERMINAL_COMMAND_ID.FIND_NEXT_TERMINAL_FOCUS, - TERMINAL_COMMAND_ID.FIND_PREVIOUS_TERMINAL_FOCUS, - TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX_TERMINAL_FOCUS, - TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS, - TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS, + TERMINAL_COMMAND_ID.FIND_NEXT, + TERMINAL_COMMAND_ID.FIND_PREVIOUS, + TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX, + TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD, + TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE, TERMINAL_COMMAND_ID.FOCUS_NEXT_PANE, TERMINAL_COMMAND_ID.FOCUS_NEXT, TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_PANE, diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 540ee8cd1c3..de24d0d0476 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -412,9 +412,7 @@ export interface ITerminalChildProcess { export const enum TERMINAL_COMMAND_ID { FIND_NEXT = 'workbench.action.terminal.findNext', - FIND_NEXT_TERMINAL_FOCUS = 'workbench.action.terminal.findNextTerminalFocus', FIND_PREVIOUS = 'workbench.action.terminal.findPrevious', - FIND_PREVIOUS_TERMINAL_FOCUS = 'workbench.action.terminal.findPreviousTerminalFocus', TOGGLE = 'workbench.action.terminal.toggleTerminal', KILL = 'workbench.action.terminal.kill', QUICK_KILL = 'workbench.action.terminal.quickKill', @@ -469,9 +467,6 @@ export const enum TERMINAL_COMMAND_ID { TOGGLE_FIND_REGEX = 'workbench.action.terminal.toggleFindRegex', TOGGLE_FIND_WHOLE_WORD = 'workbench.action.terminal.toggleFindWholeWord', TOGGLE_FIND_CASE_SENSITIVE = 'workbench.action.terminal.toggleFindCaseSensitive', - TOGGLE_FIND_REGEX_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindRegexTerminalFocus', - TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindWholeWordTerminalFocus', - TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS = 'workbench.action.terminal.toggleFindCaseSensitiveTerminalFocus', NAVIGATION_MODE_EXIT = 'workbench.action.terminal.navigationModeExit', NAVIGATION_MODE_FOCUS_NEXT = 'workbench.action.terminal.navigationModeFocusNext', NAVIGATION_MODE_FOCUS_PREVIOUS = 'workbench.action.terminal.navigationModeFocusPrevious' From 5bcd30ddf40094e3f19965eff95199143dc4f593 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 22 Oct 2019 16:21:25 -0700 Subject: [PATCH 11/89] Adopt icon font in activity bar --- .../ui/codiconLabel/codicon/codicon.css | 101 +++++++++--------- .../ui/codiconLabel/codicon/codicon.ttf | Bin 52888 -> 53184 bytes .../parts/activitybar/activitybarActions.ts | 11 +- .../parts/activitybar/activitybarPart.ts | 2 +- .../activitybar/media/activityaction.css | 17 ++- .../browser/parts/compositeBarActions.ts | 18 +++- .../debug/browser/debug.contribution.ts | 2 +- .../browser/media/debug-activity-bar.svg | 5 - .../browser/media/debug.contribution.css | 4 - .../browser/extensions.contribution.ts | 3 +- .../browser/media/extensions-activity-bar.svg | 3 - .../extensions/browser/media/extensions.css | 8 -- .../files/browser/files.contribution.ts | 2 +- .../files/browser/media/explorerviewlet.css | 5 - .../browser/media/files-activity-bar.svg | 3 - .../scm/browser/media/scm-activity-bar.svg | 10 -- .../contrib/scm/browser/media/scmViewlet.css | 4 - .../contrib/scm/browser/scm.contribution.ts | 2 +- .../browser/media/search-activity-bar.svg | 3 - .../browser/media/search.contribution.css | 9 -- .../search/browser/search.contribution.ts | 3 +- .../contrib/search/browser/searchPanel.ts | 2 +- 22 files changed, 95 insertions(+), 122 deletions(-) delete mode 100644 src/vs/workbench/contrib/debug/browser/media/debug-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/extensions/browser/media/extensions-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/extensions/browser/media/extensions.css delete mode 100644 src/vs/workbench/contrib/files/browser/media/files-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/scm/browser/media/scm-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/search-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/search.contribution.css diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index 3c635b26c1f..bec8e8b5783 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?3a05fcfc657285cdb4cd3eba790b7462") format("truetype"); + src: url("./codicon.ttf?a131ba6056e336dfd62f50ba74386e6e") format("truetype"); } .codicon[class*='codicon-'] { @@ -332,52 +332,53 @@ .codicon-search-stop:before { content: "\f1b7" } .codicon-selection:before { content: "\f1b8" } .codicon-server:before { content: "\f1b9" } -.codicon-settings:before { content: "\f1ba" } -.codicon-shield:before { content: "\f1bb" } -.codicon-smiley:before { content: "\f1bc" } -.codicon-sort-precedence:before { content: "\f1bd" } -.codicon-split-horizontal:before { content: "\f1be" } -.codicon-split-vertical:before { content: "\f1bf" } -.codicon-squirrel:before { content: "\f1c0" } -.codicon-star-full:before { content: "\f1c1" } -.codicon-star-half:before { content: "\f1c2" } -.codicon-symbol-class:before { content: "\f1c3" } -.codicon-symbol-color:before { content: "\f1c4" } -.codicon-symbol-constant:before { content: "\f1c5" } -.codicon-symbol-enum-member:before { content: "\f1c6" } -.codicon-symbol-field:before { content: "\f1c7" } -.codicon-symbol-file:before { content: "\f1c8" } -.codicon-symbol-interface:before { content: "\f1c9" } -.codicon-symbol-keyword:before { content: "\f1ca" } -.codicon-symbol-misc:before { content: "\f1cb" } -.codicon-symbol-operator:before { content: "\f1cc" } -.codicon-symbol-property:before { content: "\f1cd" } -.codicon-symbol-snippet:before { content: "\f1ce" } -.codicon-tasklist:before { content: "\f1cf" } -.codicon-telescope:before { content: "\f1d0" } -.codicon-text-size:before { content: "\f1d1" } -.codicon-three-bars:before { content: "\f1d2" } -.codicon-thumbsdown:before { content: "\f1d3" } -.codicon-thumbsup:before { content: "\f1d4" } -.codicon-tools:before { content: "\f1d5" } -.codicon-triangle-down:before { content: "\f1d6" } -.codicon-triangle-left:before { content: "\f1d7" } -.codicon-triangle-right:before { content: "\f1d8" } -.codicon-triangle-up:before { content: "\f1d9" } -.codicon-twitter:before { content: "\f1da" } -.codicon-unfold:before { content: "\f1db" } -.codicon-unlock:before { content: "\f1dc" } -.codicon-unmute:before { content: "\f1dd" } -.codicon-unverified:before { content: "\f1de" } -.codicon-verified:before { content: "\f1df" } -.codicon-versions:before { content: "\f1e0" } -.codicon-vm-active:before { content: "\f1e1" } -.codicon-vm-outline:before { content: "\f1e2" } -.codicon-vm-running:before { content: "\f1e3" } -.codicon-watch:before { content: "\f1e4" } -.codicon-whitespace:before { content: "\f1e5" } -.codicon-whole-word:before { content: "\f1e6" } -.codicon-window:before { content: "\f1e7" } -.codicon-word-wrap:before { content: "\f1e8" } -.codicon-zoom-in:before { content: "\f1e9" } -.codicon-zoom-out:before { content: "\f1ea" } +.codicon-settings-gear:before { content: "\f1ba" } +.codicon-settings:before { content: "\f1bb" } +.codicon-shield:before { content: "\f1bc" } +.codicon-smiley:before { content: "\f1bd" } +.codicon-sort-precedence:before { content: "\f1be" } +.codicon-split-horizontal:before { content: "\f1bf" } +.codicon-split-vertical:before { content: "\f1c0" } +.codicon-squirrel:before { content: "\f1c1" } +.codicon-star-full:before { content: "\f1c2" } +.codicon-star-half:before { content: "\f1c3" } +.codicon-symbol-class:before { content: "\f1c4" } +.codicon-symbol-color:before { content: "\f1c5" } +.codicon-symbol-constant:before { content: "\f1c6" } +.codicon-symbol-enum-member:before { content: "\f1c7" } +.codicon-symbol-field:before { content: "\f1c8" } +.codicon-symbol-file:before { content: "\f1c9" } +.codicon-symbol-interface:before { content: "\f1ca" } +.codicon-symbol-keyword:before { content: "\f1cb" } +.codicon-symbol-misc:before { content: "\f1cc" } +.codicon-symbol-operator:before { content: "\f1cd" } +.codicon-symbol-property:before { content: "\f1ce" } +.codicon-symbol-snippet:before { content: "\f1cf" } +.codicon-tasklist:before { content: "\f1d0" } +.codicon-telescope:before { content: "\f1d1" } +.codicon-text-size:before { content: "\f1d2" } +.codicon-three-bars:before { content: "\f1d3" } +.codicon-thumbsdown:before { content: "\f1d4" } +.codicon-thumbsup:before { content: "\f1d5" } +.codicon-tools:before { content: "\f1d6" } +.codicon-triangle-down:before { content: "\f1d7" } +.codicon-triangle-left:before { content: "\f1d8" } +.codicon-triangle-right:before { content: "\f1d9" } +.codicon-triangle-up:before { content: "\f1da" } +.codicon-twitter:before { content: "\f1db" } +.codicon-unfold:before { content: "\f1dc" } +.codicon-unlock:before { content: "\f1dd" } +.codicon-unmute:before { content: "\f1de" } +.codicon-unverified:before { content: "\f1df" } +.codicon-verified:before { content: "\f1e0" } +.codicon-versions:before { content: "\f1e1" } +.codicon-vm-active:before { content: "\f1e2" } +.codicon-vm-outline:before { content: "\f1e3" } +.codicon-vm-running:before { content: "\f1e4" } +.codicon-watch:before { content: "\f1e5" } +.codicon-whitespace:before { content: "\f1e6" } +.codicon-whole-word:before { content: "\f1e7" } +.codicon-window:before { content: "\f1e8" } +.codicon-word-wrap:before { content: "\f1e9" } +.codicon-zoom-in:before { content: "\f1ea" } +.codicon-zoom-out:before { content: "\f1eb" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index beeea24d9513097660ec7972200746989fd2bdb4..fe514c9c37046d5abf07b0caa2c819900f629f51 100644 GIT binary patch delta 1339 zcmZvae@v8h9LL|^=eh3a;0`eF9uASSJL+yoUV(S#f(|_S%>gGr95WM82`YXK$jA{; zbLfbH@-T92SW6;o+Gf{Se^8-Y$&$3(bg88IGkj3vN=qGaec)NEwVv(ud_SM>=ktB< z`My8TzCnKcU4DSro6eHdlu)Y z9zp#pYRhXX9zPhl_D$f+Bx+FC*EKekXy2juXeOY2b?=xt*$76x#OBNGeWjsE_Ylla z(+2Be9Shos0i+T@QYC# zt!jWDfr8sk=hTZDoU~i&lKTBK;(n#=`2Y^#5(aS<4kq9Xrf>n9V8tgW!cO$VhIwQ` z!}O@YejLR?9KaA$p$)ZY#v0s43I43jPE`G;-JY-;uag1jQ_6Spoo5z=jM=%46WXo6-Th5~C%1R%5 zlw)BCS@1x!bK$*c#1eI(Apl5~GISwGRWi6FwyFigRmxtGfrHj+877Dys|C#zWpy&R zAf8s2vUxE+*v$0ehTuY`5X15R$OK4l%a9$A=+{_)jDgf6 zLl!~WBSU6E+ABl0LF$zu10jhrWF@3NK-?A*uDL~VzYLlCR>-L!!+F5}xeVD1{}(d2 zD88&Svnyh^PGHx?VV#Z5h-Y=?=VqyXLWZn|e^Q2q0ROi#G!FPr%FtloKP7{QqC;Ou zqv_Ot$aG@7kgfL5P>EpCz81m9+D2WRtHbqmvNE|Pc_L+FN^8n{YbV@Fx5@2wH@OeF z@2(qKcQQ3BwIsDY_0!a;w5YW1w8?ZXy&%0cBQj%S#_5dt%%se*%(<-etbwfi*{1A< z?4j)OoS2*rkIU2U>GceGMm-a`vAGSoBYDW{$onB*n_rYalz+#&)VoIOZS{_>k6WKt z@MJ+l!DqfPL&c6Z1rYG?)BI`(a3USW^tpmgC8#VavqfcbTI^1XQ*b5-PJwsYZOmy= z*_b($SyVzO(;1mca2i=cG83GZCmad!(VT9nmaXC(f<7VM#o1S{Vp>kmCmue$rAIV< zV-kC|{1YA+9&QuLl6oAbLX)E>sZ1Ci{&zZmEM{et-Ku}uTkHxqF^uu43;%h*}q^Rq12Gf{d|NUvytW95ea~ j#*;XXTjA?IHXEeTG*&bT%`D8hZY3_U zE`&v5;xd~!yfXR1Ah=XW_@G=q7=whD_(dnhH~HOj&pFR| za?kI7=8$^epgQFpPXLqy@O*zdndyJh5(dnDK#hCn(3?A+5W7|Y*C}9jU?7!z#gTuv zSo<^6TwJg`YTCx%ypQ^U^l0{lNZ|qZy9SuDLnHmk4+BHJfO9vX_|nO2M%j$ZykE=p zp5bIV_1LjG|69Q6dM>bLMqVFPcl53S&NKm4sePxsZEg_PGV5o*oP4Re^&V{MOyJMQe5SS&yZVl)2Af#ikXDSD7kw1zBHM&)EB8&yyxRZ%sqr5bvO*3o)$QZ2ctjvl5B z^axupYc=J|ny1`WT~QQMnJvCcc51CX6DjUMh)IzR7Rja9`wu}!gHEFW9!s_)tzSK`~_lktlQPhv1Jl{lGLsp{U; peYE?ltw**lZhH#Q=Uk~Vee?dN26F3zXImBh==r5GJwI2c{tvGJQsV#s diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 973f93d7dd1..ecc12cb224b 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -270,11 +270,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const activeForegroundColor = theme.getColor(ACTIVITY_BAR_FOREGROUND); if (activeForegroundColor) { collector.addRule(` - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .action-label, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label { + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label:not(.codicon), + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .action-label:not(.codicon), + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label:not(.codicon) { background-color: ${activeForegroundColor} !important; } + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label.codicon, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .action-label.codicon, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label.codicon { + color: ${activeForegroundColor} !important; + } `); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index f509a486500..7775095b4e1 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -306,7 +306,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.globalActivityAction = new ActivityAction({ id: 'workbench.actions.manage', name: nls.localize('manage', "Manage"), - cssClass: 'update-activity' + cssClass: 'codicon-settings-gear' }); this.globalActivityActionBar.push(this.globalActivityAction); diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index eaa4f1af87a..1fb755062de 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -8,18 +8,27 @@ position: relative; padding: 5px 0; } - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label { position: relative; z-index: 1; display: flex; overflow: hidden; height: 40px; - line-height: 40px; margin-right: 0; - padding: 0 0 0 48px; box-sizing: border-box; + +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label:not(.codicon) { font-size: 15px; + line-height: 40px; + padding: 0 0 0 48px; +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label.codicon { + font-size: 24px; + align-items: center; + justify-content: center; } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before, @@ -102,7 +111,7 @@ /* Right aligned */ -.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-label { +.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-label:not(.codicon) { margin-left: 0; padding: 0 50px 0 0; background-position: calc(100% - 9px) center; diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index a2feb5fc754..8f7f10862c8 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -158,7 +158,15 @@ export class ActivityActionViewItem extends BaseActionViewItem { if (this.label) { if (this.options.icon) { const foreground = this._action.checked ? colors.activeBackgroundColor || colors.activeForegroundColor : colors.inactiveBackgroundColor || colors.inactiveForegroundColor; - this.label.style.backgroundColor = foreground ? foreground.toString() : ''; + const isExtension = this.label.className.indexOf('extensionViewlet-') >= 0; + const isRemoteExplorer = this.label.className.indexOf('remote') >= 0; + if (!isRemoteExplorer && !isExtension) { + // Apply foreground color to activity bar items (codicons) + this.label.style.color = foreground ? foreground.toString() : ''; + } else { + // Apply background color to extensions + remote explorer (svgs) + this.label.style.backgroundColor = foreground ? foreground.toString() : ''; + } } else { const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor; const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null; @@ -310,6 +318,12 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.label.className = 'action-label'; if (this.activity.cssClass) { + const isExtension = this.activity.cssClass.indexOf('extensionViewlet-') >= 0; + const isRemoteExplorer = this.activity.cssClass.indexOf('remote') >= 0; + if (this.options.icon && !isRemoteExplorer && !isExtension) { + // Only apply icon class to activity bar items (exclude extensions + remote explorer) + dom.addClass(this.label, 'codicon'); + } dom.addClass(this.label, this.activity.cssClass); } @@ -346,7 +360,7 @@ export class CompositeOverflowActivityAction extends ActivityAction { super({ id: 'additionalComposites.action', name: nls.localize('additionalViews', "Additional Views"), - cssClass: 'toggle-more' + cssClass: 'codicon-more' }); } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 3224a8678fd..3c68e97f711 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -84,7 +84,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie DebugViewlet, VIEWLET_ID, nls.localize('debug', "Debug"), - 'debug', + 'codicon-debug', 3 )); diff --git a/src/vs/workbench/contrib/debug/browser/media/debug-activity-bar.svg b/src/vs/workbench/contrib/debug/browser/media/debug-activity-bar.svg deleted file mode 100644 index fcb9413c8c2..00000000000 --- a/src/vs/workbench/contrib/debug/browser/media/debug-activity-bar.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index f0ae2317869..019480191f2 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -4,10 +4,6 @@ *--------------------------------------------------------------------------------------------*/ /* Activity Bar */ -.monaco-workbench .activitybar .monaco-action-bar .action-label.debug { - -webkit-mask: url('debug-activity-bar.svg') no-repeat 50% 50%; -} - .monaco-editor .debug-top-stack-frame-column::before { background: url('current-arrow.svg') center center no-repeat; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 0efac2f4a02..ebcd60b6ef0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/extensions'; import { localize } from 'vs/nls'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -81,7 +80,7 @@ const viewletDescriptor = new ViewletDescriptor( ExtensionsViewlet, VIEWLET_ID, localize('extensions', "Extensions"), - 'extensions', + 'codicon-extensions', 4 ); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensions-activity-bar.svg b/src/vs/workbench/contrib/extensions/browser/media/extensions-activity-bar.svg deleted file mode 100644 index 93e4b554dab..00000000000 --- a/src/vs/workbench/contrib/extensions/browser/media/extensions-activity-bar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensions.css b/src/vs/workbench/contrib/extensions/browser/media/extensions.css deleted file mode 100644 index ad1858f6f05..00000000000 --- a/src/vs/workbench/contrib/extensions/browser/media/extensions.css +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.extensions { - -webkit-mask: url('extensions-activity-bar.svg') no-repeat 50% 50%; -} diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index d76b878beb9..350dd14c5f4 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -77,7 +77,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie ExplorerViewlet, VIEWLET_ID, nls.localize('explore', "Explorer"), - 'explore', + 'codicon-files', 0 )); diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 976b42abd13..d4bd6709d05 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -3,11 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* Activity Bar */ -.monaco-workbench .activitybar .monaco-action-bar .action-label.explore { - -webkit-mask: url('files-activity-bar.svg') no-repeat 50% 50%; -} - /* --- Explorer viewlet --- */ .explorer-viewlet, .explorer-folders-view { diff --git a/src/vs/workbench/contrib/files/browser/media/files-activity-bar.svg b/src/vs/workbench/contrib/files/browser/media/files-activity-bar.svg deleted file mode 100644 index c109b13c3d2..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/files-activity-bar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/scm/browser/media/scm-activity-bar.svg b/src/vs/workbench/contrib/scm/browser/media/scm-activity-bar.svg deleted file mode 100644 index 5092b857329..00000000000 --- a/src/vs/workbench/contrib/scm/browser/media/scm-activity-bar.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 3a915b29ba8..5447fd40cd3 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -3,10 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .activitybar > .content .monaco-action-bar .action-label.scm { - -webkit-mask: url('scm-activity-bar.svg') no-repeat 50% 50%; -} - .monaco-workbench .viewlet.scm-viewlet .collapsible.header .actions { width: initial; flex: 1; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index ff7e8d6bae6..9606395dd86 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -42,7 +42,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie SCMViewlet, VIEWLET_ID, localize('source control', "Source Control"), - 'scm', + 'codicon-source-control', 2 )); diff --git a/src/vs/workbench/contrib/search/browser/media/search-activity-bar.svg b/src/vs/workbench/contrib/search/browser/media/search-activity-bar.svg deleted file mode 100644 index a7ea9ab29ab..00000000000 --- a/src/vs/workbench/contrib/search/browser/media/search-activity-bar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/search/browser/media/search.contribution.css b/src/vs/workbench/contrib/search/browser/media/search.contribution.css deleted file mode 100644 index d0c241f8fb5..00000000000 --- a/src/vs/workbench/contrib/search/browser/media/search.contribution.css +++ /dev/null @@ -1,9 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/* Activity Bar */ -.monaco-workbench .activitybar .monaco-action-bar .action-label.search { - -webkit-mask: url('search-activity-bar.svg') no-repeat 50% 50%; -} \ No newline at end of file diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index d8d379688d3..697ceeb031a 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -11,7 +11,6 @@ import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import 'vs/css!./media/search.contribution'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { getSelectionSearchString } from 'vs/editor/contrib/find/findController'; @@ -507,7 +506,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie SearchViewlet, VIEWLET_ID, nls.localize('name', "Search"), - 'search', + 'codicon-search', 1 )); diff --git a/src/vs/workbench/contrib/search/browser/searchPanel.ts b/src/vs/workbench/contrib/search/browser/searchPanel.ts index 55037157026..0c0def10162 100644 --- a/src/vs/workbench/contrib/search/browser/searchPanel.ts +++ b/src/vs/workbench/contrib/search/browser/searchPanel.ts @@ -67,4 +67,4 @@ export class SearchPanel extends Panel { getSearchView(): SearchView { return this.searchView; } -} \ No newline at end of file +} From a7c1ece76543e44621ad71d99fe81706eaf320c4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 11:16:58 +0200 Subject: [PATCH 12/89] adjust tests that relied on URI.toJSON or URI.toString output --- .../debug/test/common/debugSource.test.ts | 2 +- .../test/node/ripgrepTextSearchEngine.test.ts | 25 +++++++++++++------ .../electron-browser/api/extHostTypes.test.ts | 4 ++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts b/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts index f44747a9fcf..33073efdda3 100644 --- a/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/debugSource.test.ts @@ -36,7 +36,7 @@ suite('Debug - Source', () => { assert.equal(source.name, 'internalModule.js'); assert.equal(source.inMemory, true); assert.equal(source.reference, 11); - assert.equal(source.uri.toString(), 'debug:internalModule.js?session%3DaDebugSessionId%26ref%3D11'); + assert.equal(source.uri.toString(), 'debug:internalModule.js?session=aDebugSessionId&ref=11'); }); test('get encoded debug data', () => { diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts index d1b0f31042f..2c89f7bc9d4 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -72,6 +72,17 @@ suite('RipgrepTextSearchEngine', () => { suite('RipgrepParser', () => { const TEST_FOLDER = URI.file('/foo/bar'); + function joinPathExt(uri: URI, path: string): URI { + const result = joinPath(uri, path); + result.toString(); + // ^^^^^^^^ + // doing this to init the URI._formatted-field because this + // test compares the output of URI.toJSON and because + // calling URI.file (called by RipgrepParser) will also + // initialize URI._formatted + return result; + } + function testParser(inputData: string[], expectedResults: TextSearchResult[]): void { const testParser = new RipgrepParser(1000, TEST_FOLDER.fsPath); @@ -119,7 +130,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'file1.js'), + uri: joinPathExt(TEST_FOLDER, 'file1.js'), ranges: [new Range(3, 3, 3, 6)] } ]); @@ -138,7 +149,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'file1.js'), + uri: joinPathExt(TEST_FOLDER, 'file1.js'), ranges: [new Range(3, 3, 3, 6)] }, { @@ -146,7 +157,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'app/file2.js'), + uri: joinPathExt(TEST_FOLDER, 'app/file2.js'), ranges: [new Range(3, 3, 3, 6)] }, { @@ -154,7 +165,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'app2/file3.js'), + uri: joinPathExt(TEST_FOLDER, 'app2/file3.js'), ranges: [new Range(3, 3, 3, 6)] } ]); @@ -183,7 +194,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foo bar', matches: [new Range(0, 3, 0, 7)] }, - uri: joinPath(TEST_FOLDER, 'file1.js'), + uri: joinPathExt(TEST_FOLDER, 'file1.js'), ranges: [new Range(3, 3, 3, 7)] }, { @@ -191,7 +202,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'app/file2.js'), + uri: joinPathExt(TEST_FOLDER, 'app/file2.js'), ranges: [new Range(3, 3, 3, 6)] }, { @@ -199,7 +210,7 @@ suite('RipgrepTextSearchEngine', () => { text: 'foobar', matches: [new Range(0, 3, 0, 6)] }, - uri: joinPath(TEST_FOLDER, 'app2/file3.js'), + uri: joinPathExt(TEST_FOLDER, 'app2/file3.js'), ranges: [new Range(3, 3, 3, 6)] } ]); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index f33a4d5f6e6..05321a9875d 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -22,7 +22,8 @@ suite('ExtHostTypes', function () { assert.deepEqual(uri.toJSON(), { $mid: 1, scheme: 'file', - path: '/path/test.file' + path: '/path/test.file', + external: 'file:///path/test.file' }); assert.ok(uri.fsPath); @@ -30,6 +31,7 @@ suite('ExtHostTypes', function () { $mid: 1, scheme: 'file', path: '/path/test.file', + external: 'file:///path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), _sep: isWindows ? 1 : undefined, }); From bea884b934004ad3974b3ac3988ca271bf0cd54c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 12:04:40 +0200 Subject: [PATCH 13/89] polish --- src/vs/base/common/uri.ts | 53 ++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index ff4fd6c86ff..d8f0b712897 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -375,7 +375,7 @@ export class URI implements UriComponents { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding: boolean = false): string { - return _toString(skipEncoding ? minialEncode : normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(skipEncoding ? minimalEncode : normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); } toJSON(): UriComponents { @@ -433,7 +433,7 @@ class _URI extends URI { toString(skipEncoding: boolean = false): string { if (skipEncoding) { // we don't cache that - return _toString(minialEncode, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(minimalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); } if (!this._formatted) { this._formatted = _toString(normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); @@ -527,8 +527,8 @@ function percentDecode(str: string): string { //#region ---- encode // https://url.spec.whatwg.org/#percent-encoded-bytes -// "The C0 control percent-encode set are the C0 controls and all code points greater than U+007E (~)." +// "The C0 control percent-encode set are the C0 controls and all code points greater than U+007E (~)." function isC0ControlPercentEncodeSet(code: number): boolean { return code <= 0x1F || code > 0x7E; } @@ -566,28 +566,41 @@ function isLowerAsciiHex(code: number): boolean { || code >= CharCode.a && code <= CharCode.z; } +const _encodeTable: string[] = (function () { + let table: string[] = []; + for (let code = 0; code < 128; code++) { + table[code] = `%${code.toString(16)}`; + } + return table; +})(); + function percentEncode(str: string, mustEncode: (code: number) => boolean): string { let lazyOutStr: string | undefined; - for (let i = 0; i < str.length; i++) { - const code = str.charCodeAt(i); + for (let pos = 0; pos < str.length; pos++) { + const code = str.charCodeAt(pos); // invoke encodeURIComponent when needed if (mustEncode(code)) { if (!lazyOutStr) { - lazyOutStr = str.substr(0, i); + lazyOutStr = str.substr(0, pos); } - // if (isHighSurrogate(code)) { - if (i + 1 < str.length && isLowSurrogate(str.charCodeAt(i + 1))) { - lazyOutStr += encodeURIComponent(str.substr(i, 2)); - i += 1; + // Append encoded version of this surrogate pair (2 characters) + if (pos + 1 < str.length && isLowSurrogate(str.charCodeAt(pos + 1))) { + lazyOutStr += encodeURIComponent(str.substr(pos, 2)); + pos += 1; } else { // broken surrogate pair - lazyOutStr += str.charAt(i); + lazyOutStr += str.charAt(pos); } } else { - // todo@joh PERF, use lookup table - lazyOutStr += encodeURIComponent(str.charAt(i)); + // Append encoded version of the current character, use lookup table + // to speed up repeated encoding of the same characters. + if (code < _encodeTable.length) { + lazyOutStr += _encodeTable[code]; + } else { + lazyOutStr += encodeURIComponent(str.charAt(pos)); + } } continue; } @@ -595,20 +608,20 @@ function percentEncode(str: string, mustEncode: (code: number) => boolean): stri // normalize percent encoded sequences to upper case // todo@joh also changes invalid sequences if (code === CharCode.PercentSign - && i + 2 < str.length - && (isLowerAsciiHex(str.charCodeAt(i + 1)) || isLowerAsciiHex(str.charCodeAt(i + 2))) + && pos + 2 < str.length + && (isLowerAsciiHex(str.charCodeAt(pos + 1)) || isLowerAsciiHex(str.charCodeAt(pos + 2))) ) { if (!lazyOutStr) { - lazyOutStr = str.substr(0, i); + lazyOutStr = str.substr(0, pos); } - lazyOutStr += '%' + str.substr(i + 1, 2).toUpperCase(); - i += 2; + lazyOutStr += '%' + str.substr(pos + 1, 2).toUpperCase(); + pos += 2; continue; } // once started, continue to build up lazy output if (lazyOutStr) { - lazyOutStr += str.charAt(i); + lazyOutStr += str.charAt(pos); } } return lazyOutStr || str; @@ -618,7 +631,7 @@ const enum EncodePart { user, authority, path, query, fragment } const normalEncode: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; -const minialEncode: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; +const minimalEncode: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; /** * Create the external version of a uri From 96aee2604d0b894cb2c395eb866b11208965555d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 12:55:07 +0200 Subject: [PATCH 14/89] add tests that compare URI against nodejs/standard URL --- src/vs/base/test/node/uri.test.ts | 85 ++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 6d948736b0c..8141b7e1c0c 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; -import { pathToFileURL } from 'url'; +import { pathToFileURL, fileURLToPath, URL } from 'url'; suite('URI', () => { test('file#toString', () => { @@ -455,7 +455,7 @@ suite('URI', () => { assert.equal(uri1.path, uri2.path); assert.equal(uri1.query, uri2.query); assert.equal(uri1.fragment, uri2.fragment); - assert.equal(strIn, strOut); // fails here!! + assert.equal(strIn, strOut); }); test('Uri#parse can break path-component #45515', function () { @@ -469,7 +469,7 @@ suite('URI', () => { assert.equal(uri1.path, uri2.path); assert.equal(uri1.query, uri2.query); assert.equal(uri1.fragment, uri2.fragment); - assert.equal(strIn, strOut); // fails here!! + assert.equal(strIn, strOut); }); test('URI - (de)serialize', function () { @@ -502,14 +502,27 @@ suite('URI', () => { // console.profileEnd(); }); - function assertFileUri(path: string): void { - // check that our uri aligns with nodejs + // ------ check against standard URL and nodejs-file-url utils + + function assertFileUri(path: string, recurse = true): void { const actual = URI.file(path).toString(); const expected = pathToFileURL(path).href; assert.equal(actual, expected); + if (recurse) { + assertFsPath(expected, false); + } } - test('URI.file vs pathToFileURL', function () { + function assertFsPath(uri: string, recurse = true): void { + const actual = URI.parse(uri).fsPath; + const expected = fileURLToPath(uri); + assert.equal(actual, expected); + if (recurse) { + assertFileUri(actual, false); + } + } + + test('URI.file and pathToFileURL', function () { assertFileUri('/foo/bar'); assertFileUri('/foo/%2e.txt'); // %2e -> . assertFileUri('/foo/%A0.txt'); // %A0 -> invalid @@ -520,4 +533,64 @@ suite('URI', () => { assertFileUri('/c\\win\\path'); } }); + + test('URI.fsPath and fileURLToPath', function () { + assertFsPath('file:///foo/bar'); + assertFsPath('file:///fo%25/bar'); + assertFsPath('file:///foo/b ar/text.cs'); + assertFsPath('file:///foö/bar'); + assertFsPath('file:///fo%C3%B6/bar'); + assertFsPath('file:///'); + + // assertFsPath('file:///fo%2f/bar'); not allowed in nodejs + + if (isWindows) { + // nodejs doesn't create UNC-paths on non-windows + assertFsPath('file://unc-host/foö/bar'); + assertFsPath('file://unc-host/'); + + // nodejs prepends c: with / on non-windows + assertFsPath('file:///c:/bar/foo'); + } + + if (!isWindows) { + // + assertFsPath('file:///foo%5cbar'); + assertFsPath('file:///foo%5Cbar'); + assertFsPath('file:///foo%5C%5cbar'); + } + }); + + function assertToString(uri: string): void { + const actual = URI.parse(uri).toString(); + const expected = new URL(uri).href; + assert.equal(actual, expected); + } + + test('URI.toString and URL.href', function () { + assertToString('before:some/file/path'); + assertToString('scheme://authority/path'); + assertToString('scheme:/path'); + assertToString('foo:bar/path'); + // assertToString('http:/api/files/test.me?t=1234'); // URL make api the hostname, + assertToString('http://api/files/test.me?t=1234'); + // assertToString('file:///c:/test/me'); // we encode the colon + // assertToString('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins/c%23/plugin.json'); + // assertToString('file:///c:/test %25/path'); + assertToString('file://shares/files/c%23/p.cs'); + assertToString('inmemory:'); + assertToString('foo:api/files/test'); + assertToString('file:?q'); + assertToString('file:#d'); + assertToString('f3ile:#d'); + assertToString('foo+bar:path'); + assertToString('foo-bar:path'); + assertToString('foo.bar:path'); + assertToString('file:///_:/path'); + assertToString('https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'); + assertToString('https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'); + assertToString('debug:internalModule.js?session=aDebugSessionId&ref=11'); + assertToString('debug:internalModule.js?session%3DaDebugSessionId%26ref%3D11'); + assertToString('https://github.com/microsoft/vscode/issues/33746#issuecomment-545345356'); + }); }); From 612fdf5b891219d3ded21e7b1254ce1e01308998 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 13:58:37 +0200 Subject: [PATCH 15/89] fix missing #-encode in URI.file factory --- src/vs/base/common/uri.ts | 1 + src/vs/base/test/node/uri.test.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index d8f0b712897..5a4c93faffc 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -343,6 +343,7 @@ export class URI implements UriComponents { // escape some vital characters authority = authority.replace(/%/g, '%25'); path = path.replace(/%/g, '%25'); + path = path.replace(/#/g, '%23'); if (!isWindows) { path = path.replace(/\\/g, '%5C'); diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 8141b7e1c0c..dfb9f76e67b 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -528,6 +528,7 @@ suite('URI', () => { assertFileUri('/foo/%A0.txt'); // %A0 -> invalid assertFileUri('/foo/ü.txt'); assertFileUri('/foo/ß.txt'); + assertFileUri('/my/c#project/d.cs'); // assertFileUri('foo'); nodejs resolves the path first if (!isWindows) { assertFileUri('/c\\win\\path'); @@ -541,6 +542,7 @@ suite('URI', () => { assertFsPath('file:///foö/bar'); assertFsPath('file:///fo%C3%B6/bar'); assertFsPath('file:///'); + assertFsPath('file:///my/c%23project/c.cs'); // assertFsPath('file:///fo%2f/bar'); not allowed in nodejs From 50d97cf824eb8fef6f5682d3e96ebfe0bf8cb330 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 15:40:30 +0200 Subject: [PATCH 16/89] tweak tests, make sure nodejs tests are platform aware --- src/vs/base/test/node/uri.test.ts | 117 +++++++++++++++++++----------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index dfb9f76e67b..a4ac8f4f03e 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; -import { pathToFileURL, fileURLToPath, URL } from 'url'; +import { pathToFileURL, fileURLToPath } from 'url'; suite('URI', () => { test('file#toString', () => { @@ -254,7 +254,7 @@ suite('URI', () => { assert.equal(value.fsPath, '\\\\localhost\\c$\\GitDevelopment\\express'); assert.equal(value.query, ''); assert.equal(value.fragment, ''); - assert.equal(value.toString(), 'file://localhost/c%24/GitDevelopment/express'); + assert.equal(value.toString(), 'file://localhost/c$/GitDevelopment/express'); value = URI.file('c:\\test with %\\path'); assert.equal(value.path, '/c:/test with %/path'); @@ -435,7 +435,7 @@ suite('URI', () => { assert.equal(uriFromStr.toString(), 'file:///foo/%25A0.txt'); }); - test('Valid percent-encoded character in filename', function () { + test('Unable to open \'%2e.txt\'', function () { let uriFromPath = URI.file('/foo/%2e.txt'); let uriFromStr = URI.parse(uriFromPath.toString()); assert.equal(uriFromPath.scheme, uriFromStr.scheme); @@ -472,6 +472,17 @@ suite('URI', () => { assert.equal(strIn, strOut); }); + test('Uri#parse can break path-component #45515, part 2', function () { + let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; + let uri1 = URI.parse(strIn); + assert.equal(uri1.toString(), strIn); + assert.equal(uri1.scheme, 'https'); + assert.equal(uri1.authority, 'firebasestorage.googleapis.com'); + assert.equal(uri1.path, '/v0/b/brewlangerie.appspot.com/o/products/zVNZkudXJyq8bPGTXUxx/Betterave-Sesame.jpg'); // INCORRECT: %2F got decoded but for compat reasons we cannot change this anymore... + assert.equal(uri1.query, 'alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'); + assert.equal(uri1.fragment, ''); + }); + test('URI - (de)serialize', function () { const values = [ @@ -504,65 +515,89 @@ suite('URI', () => { // ------ check against standard URL and nodejs-file-url utils - function assertFileUri(path: string, recurse = true): void { + function assertUriFromFsPath(path: string, recurse = true): void { const actual = URI.file(path).toString(); const expected = pathToFileURL(path).href; assert.equal(actual, expected); if (recurse) { - assertFsPath(expected, false); + assertFsPathFromUri(expected, false); } } - function assertFsPath(uri: string, recurse = true): void { + function assertFsPathFromUri(uri: string, recurse = true): void { const actual = URI.parse(uri).fsPath; const expected = fileURLToPath(uri); assert.equal(actual, expected); if (recurse) { - assertFileUri(actual, false); + assertUriFromFsPath(actual, false); } } test('URI.file and pathToFileURL', function () { - assertFileUri('/foo/bar'); - assertFileUri('/foo/%2e.txt'); // %2e -> . - assertFileUri('/foo/%A0.txt'); // %A0 -> invalid - assertFileUri('/foo/ü.txt'); - assertFileUri('/foo/ß.txt'); - assertFileUri('/my/c#project/d.cs'); - // assertFileUri('foo'); nodejs resolves the path first - if (!isWindows) { - assertFileUri('/c\\win\\path'); - } + const posixPaths = [ + '/foo/bar', + '/foo/%2e.txt', + '/foo/%A0.txt', + '/foo/ü.txt', + '/foo/ß.txt', + '/my/c#project/d.cs', + '/c\\win\\path' + ]; + const windowsPaths = [ + 'd:/foo/bar', + 'd:/foo/%2e.txt', + 'd:/foo/%A0.txt', + 'd:/foo/ü.txt', + 'd:/foo/ß.txt', + 'd:/my/c#project/d.cs', + 'c:\\win\\path', + 'c:\\test\\drive', + '\\\\shäres\\path\\c#\\plugin.json', + '\\\\localhost\\c$\\GitDevelopment\\express', + 'c:\\test with %\\path', + 'c:\\test with %25\\path', + 'c:\\test with %25\\c#code', + '\\\\shares', + '\\\\shares\\', + ]; + + // nodejs is strict to the platform on which it runs + (isWindows ? windowsPaths : posixPaths).forEach(p => assertUriFromFsPath(p)); }); + test('URI.fsPath and fileURLToPath', function () { - assertFsPath('file:///foo/bar'); - assertFsPath('file:///fo%25/bar'); - assertFsPath('file:///foo/b ar/text.cs'); - assertFsPath('file:///foö/bar'); - assertFsPath('file:///fo%C3%B6/bar'); - assertFsPath('file:///'); - assertFsPath('file:///my/c%23project/c.cs'); + const posixUris = [ + 'file:///foo/bar', + 'file:///fo%25/bar', + 'file:///foo/b ar/text.cs', + 'file:///foö/bar', + 'file:///fo%C3%B6/bar', + 'file:///', + 'file:///my/c%23project/c.cs', + 'file:///foo%5cbar', + 'file:///foo%5Cbar', + 'file:///foo%5C%5cbar', + ]; + const windowsUris = [ + 'file:///f:/foo/bar', + 'file:///f:/fo%25/bar', + 'file:///f:/foo/b ar/text.cs', + 'file:///f:/foö/bar', + 'file:///f:/fo%C3%B6/bar', + 'file:///f:/', + 'file:///f:/my/c%23project/c.cs', + 'file://unc-host/foö/bar', + 'file://unc-host/', + 'file:///c:/bar/foo', + ]; - // assertFsPath('file:///fo%2f/bar'); not allowed in nodejs - - if (isWindows) { - // nodejs doesn't create UNC-paths on non-windows - assertFsPath('file://unc-host/foö/bar'); - assertFsPath('file://unc-host/'); - - // nodejs prepends c: with / on non-windows - assertFsPath('file:///c:/bar/foo'); - } - - if (!isWindows) { - // - assertFsPath('file:///foo%5cbar'); - assertFsPath('file:///foo%5Cbar'); - assertFsPath('file:///foo%5C%5cbar'); - } + // nodejs is strict to the platform on which it runs + (isWindows ? windowsUris : posixUris).forEach(u => assertFsPathFromUri(u)); }); + // ---- check against standard url + function assertToString(uri: string): void { const actual = URI.parse(uri).toString(); const expected = new URL(uri).href; From cc7fdb6089ef1a7c04ea99953e27ef3a476a6070 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 16:07:59 +0200 Subject: [PATCH 17/89] more tests, check more sample against standard url, make sure we normalize well --- src/vs/base/common/uri.ts | 2 +- src/vs/base/test/node/uri.test.ts | 49 +++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 5a4c93faffc..b66eb043f72 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -678,7 +678,7 @@ function _toString(encoder: { (code: number): boolean }[], scheme: string, autho // lower-case windows drive letters in /C:/fff or C:/fff and escape `:` - let match = /(\/?[a-zA-Z]):/.exec(pathEncoded); // <- todo@joh make fast! + let match = /(\/?[a-z])(:|%3a)/i.exec(pathEncoded); // <- todo@joh make fast! if (match) { pathEncoded = match[1].toLowerCase() + '%3A' + pathEncoded.substr(match[0].length); } diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index a4ac8f4f03e..4c18bbda023 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; @@ -483,6 +484,28 @@ suite('URI', () => { assert.equal(uri1.fragment, ''); }); + test('URI#parse creates normalized output', function () { + function assertToString(input: string, output: string = input): void { + const uri = URI.parse(input); + assert.equal(uri.toString(), output); + } + + // don't break query string, encoded characters + assertToString('https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'); + assertToString('https://go.microsoft.com/fwlink/?LinkId=518008'); + assertToString('https://twitter.com/search?src=typd&q=%23tag'); + assertToString('http://localhost:3000/#/foo?bar=baz'); + assertToString('https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'); + assertToString('https://myhost.com/Redirect?url=http%3a%2f%2Fwww.bing.com%3Fsearch%3Dtom', 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'); // upper-case hex + assertToString('https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü', 'https://go.microsoft.com/fwlink/?LinkId=518008&fo%C3%B6&k%C3%A9%C2%A5=%C3%BC%C3%BC'); // encode umlaute and friends + + // normalize things like + assertToString('file:///c:/test/me', 'file:///c%3A/test/me'); // drive letter treatment + assertToString('file:///C:/test/me', 'file:///c%3A/test/me'); + assertToString('file:///c%3A/test/me', 'file:///c%3A/test/me'); + assertToString('file:///C%3A/test/me', 'file:///c%3A/test/me'); + }); + test('URI - (de)serialize', function () { const values = [ @@ -578,6 +601,9 @@ suite('URI', () => { 'file:///foo%5cbar', 'file:///foo%5Cbar', 'file:///foo%5C%5cbar', + 'file:///Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', + 'file:///monacotools/folder/isi.txt', + 'file:///monacotools1/certificates/SSL/', ]; const windowsUris = [ 'file:///f:/foo/bar', @@ -590,6 +616,10 @@ suite('URI', () => { 'file://unc-host/foö/bar', 'file://unc-host/', 'file:///c:/bar/foo', + 'file:///c:/alex.txt', + 'file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', + 'file://monacotools/folder/isi.txt', + 'file://monacotools1/certificates/SSL/', ]; // nodejs is strict to the platform on which it runs @@ -597,14 +627,14 @@ suite('URI', () => { }); // ---- check against standard url + test('URI.toString equals (whatwg) URL.toString', function () { - function assertToString(uri: string): void { - const actual = URI.parse(uri).toString(); - const expected = new URL(uri).href; - assert.equal(actual, expected); - } + function assertToString(uri: string): void { + const actual = URI.parse(uri).toString(); + const expected = new URL(uri).href; + assert.equal(actual, expected); + } - test('URI.toString and URL.href', function () { assertToString('before:some/file/path'); assertToString('scheme://authority/path'); assertToString('scheme:/path'); @@ -629,5 +659,12 @@ suite('URI', () => { assertToString('debug:internalModule.js?session=aDebugSessionId&ref=11'); assertToString('debug:internalModule.js?session%3DaDebugSessionId%26ref%3D11'); assertToString('https://github.com/microsoft/vscode/issues/33746#issuecomment-545345356'); + assertToString('http://localhost:3000/#/foo?bar=baz'); + assertToString('https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'); + assertToString('https://myhost.com/my/pãth/ìß/hē®ę'); + assertToString('http://foo:bar@localhost/far'); + assertToString('http://foo@localhost/far'); + assertToString('http://foo:bAr@localhost:8080/far'); + assertToString('http://foo@localhost:8080/far'); }); }); From ff9818b875bf5fd5dcd4637df09642db715447ba Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 23 Oct 2019 15:15:45 +0100 Subject: [PATCH 18/89] GitHub Actions: only run on master branch --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55b4b4ba377..985007e1f52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,12 @@ name: CI -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: linux: From 989f98a67e00061174d72c2255d49f4948ff605b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 16:16:54 +0200 Subject: [PATCH 19/89] tweak nodejs compare tests so that they work on windows --- src/vs/base/test/node/uri.test.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 4c18bbda023..6901373fb5c 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -539,8 +539,13 @@ suite('URI', () => { // ------ check against standard URL and nodejs-file-url utils function assertUriFromFsPath(path: string, recurse = true): void { - const actual = URI.file(path).toString(); - const expected = pathToFileURL(path).href; + let actual = URI.file(path).toString(); + if (isWindows) { + // we always encode windows drive letters and since nodejs + // never does. to compare we need to undo our encoding... + actual = actual.replace(/(\/[a-z])%3A/, '$1:'); + } + let expected = pathToFileURL(path).href; assert.equal(actual, expected); if (recurse) { assertFsPathFromUri(expected, false); @@ -548,8 +553,8 @@ suite('URI', () => { } function assertFsPathFromUri(uri: string, recurse = true): void { - const actual = URI.parse(uri).fsPath; - const expected = fileURLToPath(uri); + let actual = URI.parse(uri).fsPath; + let expected = fileURLToPath(uri); assert.equal(actual, expected); if (recurse) { assertUriFromFsPath(actual, false); From 59cd7f5a74ec194e7aeb4dbc31d96c4f39b613a7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 16:36:41 +0200 Subject: [PATCH 20/89] cache regular expression, inline string constants --- src/vs/base/common/uri.ts | 120 +++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index b66eb043f72..2a925eabbfd 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -7,9 +7,7 @@ import { isWindows } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; import { isHighSurrogate, isLowSurrogate } from 'vs/base/common/strings'; -const _schemePattern = /^\w[\w\d+.-]*$/; -const _singleSlashStart = /^\//; -const _doubleSlashStart = /^\/\//; +const _schemeRegExp = /^\w[\w\d+.-]*$/; function _validateUri(ret: URI): void { @@ -20,7 +18,7 @@ function _validateUri(ret: URI): void { // scheme, https://tools.ietf.org/html/rfc3986#section-3.1 // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - if (ret.scheme && !_schemePattern.test(ret.scheme)) { + if (ret.scheme && !_schemeRegExp.test(ret.scheme)) { throw new Error('[UriError]: Scheme contains illegal characters.'); } @@ -31,11 +29,11 @@ function _validateUri(ret: URI): void { // with two slash characters ("//"). if (ret.path) { if (ret.authority) { - if (!_singleSlashStart.test(ret.path)) { + if (ret.path.charCodeAt(0) !== CharCode.Slash) { throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); } } else { - if (_doubleSlashStart.test(ret.path)) { + if (ret.path.charCodeAt(0) === CharCode.Slash && ret.path.charCodeAt(1) === CharCode.Slash) { throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); } } @@ -63,19 +61,16 @@ function _referenceResolution(scheme: string, path: string): string { case 'http': case 'file': if (!path) { - path = _slash; - } else if (path[0] !== _slash) { - path = _slash + path; + path = '/'; + } else if (path[0].charCodeAt(0) !== CharCode.Slash) { + path = '/' + path; } break; } return path; } -const _empty = ''; -const _slash = '/'; - -const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; +const _uriRegExp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; const enum MatchIndex { scheme = 2, @@ -85,6 +80,11 @@ const enum MatchIndex { fragment = 9 } +const _percentRegExp = /%/g; +const _hashRegExp = /#/g; +const _backslashRegExp = /\\/g; +const _slashRegExp = /\//g; + /** * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. * This class is a simple parser which creates the basic component parts @@ -161,20 +161,20 @@ export class URI implements UriComponents { protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) { if (typeof schemeOrData === 'object') { - this.scheme = schemeOrData.scheme || _empty; - this.authority = schemeOrData.authority || _empty; - this.path = schemeOrData.path || _empty; - this.query = schemeOrData.query || _empty; - this.fragment = schemeOrData.fragment || _empty; + this.scheme = schemeOrData.scheme || ''; + this.authority = schemeOrData.authority || ''; + this.path = schemeOrData.path || ''; + this.query = schemeOrData.query || ''; + this.fragment = schemeOrData.fragment || ''; // no validation because it's this URI // that creates uri components. // _validateUri(this); } else { this.scheme = _schemeFix(schemeOrData); - this.authority = authority || _empty; - this.path = _referenceResolution(this.scheme, path || _empty); - this.query = query || _empty; - this.fragment = fragment || _empty; + this.authority = authority || ''; + this.path = _referenceResolution(this.scheme, path || ''); + this.query = query || ''; + this.fragment = fragment || ''; _validateUri(this); } @@ -225,27 +225,27 @@ export class URI implements UriComponents { if (scheme === undefined) { scheme = this.scheme; } else if (scheme === null) { - scheme = _empty; + scheme = ''; } if (authority === undefined) { authority = this.authority; } else if (authority === null) { - authority = _empty; + authority = ''; } if (path === undefined) { path = this.path; } else if (path === null) { - path = _empty; + path = ''; } if (query === undefined) { query = this.query; } else if (query === null) { - query = _empty; + query = ''; } if (fragment === undefined) { fragment = this.fragment; } else if (fragment === null) { - fragment = _empty; + fragment = ''; } if (scheme === this.scheme @@ -269,16 +269,16 @@ export class URI implements UriComponents { * @param value A string which represents an URI (see `URI#toString`). */ static parse(value: string): URI { - const match = _regexp.exec(value); + const match = _uriRegExp.exec(value); if (!match) { throw new Error(`[UriError]: Invalid input: ${value}`); } - const scheme = _schemeFix(match[MatchIndex.scheme]) || _empty; - const authority = match[MatchIndex.authority] || _empty; - const path = _referenceResolution(scheme, match[MatchIndex.path] || _empty); - const query = match[MatchIndex.query] || _empty; - const fragment = match[MatchIndex.fragment] || _empty; + const scheme = _schemeFix(match[MatchIndex.scheme]) || ''; + const authority = match[MatchIndex.authority] || ''; + const path = _referenceResolution(scheme, match[MatchIndex.path] || ''); + const query = match[MatchIndex.query] || ''; + const fragment = match[MatchIndex.fragment] || ''; const result = new _URI( scheme, @@ -287,7 +287,7 @@ export class URI implements UriComponents { percentDecode(query), percentDecode(fragment), ); - result._formatted = _toString(normalEncode, scheme, authority, path, query, fragment); + result._formatted = _toString(_normalEncoder, scheme, authority, path, query, fragment); return result; } @@ -314,39 +314,38 @@ export class URI implements UriComponents { */ static file(path: string): URI { - let authority = _empty; + let authority = ''; // normalize to fwd-slashes on windows, // on other systems bwd-slashes are valid // filename character, eg /f\oo/ba\r.txt if (isWindows) { - path = path.replace(/\\/g, _slash); + path = path.replace(_backslashRegExp, '/'); } // check for authority as used in UNC shares // or use the path as given - if (path[0] === _slash && path[1] === _slash) { - const idx = path.indexOf(_slash, 2); + if (path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(1) === CharCode.Slash) { + const idx = path.indexOf('/', 2); if (idx === -1) { authority = path.substring(2); - path = _slash; + path = '/'; } else { authority = path.substring(2, idx); - path = path.substring(idx) || _slash; + path = path.substring(idx) || '/'; } } - if (path.charAt(0) !== _slash) { - path = _slash + path; - } + // ensures that path starts with / + path = _referenceResolution('file', path); // escape some vital characters - authority = authority.replace(/%/g, '%25'); - path = path.replace(/%/g, '%25'); - path = path.replace(/#/g, '%23'); + authority = authority.replace(_percentRegExp, '%25'); + path = path.replace(_percentRegExp, '%25'); + path = path.replace(_hashRegExp, '%23'); if (!isWindows) { - path = path.replace(/\\/g, '%5C'); + path = path.replace(_backslashRegExp, '%5C'); } return URI.parse('file://' + authority + path); @@ -376,7 +375,7 @@ export class URI implements UriComponents { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding: boolean = false): string { - return _toString(skipEncoding ? minimalEncode : normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(skipEncoding ? _minimalEncoder : _normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } toJSON(): UriComponents { @@ -434,10 +433,10 @@ class _URI extends URI { toString(skipEncoding: boolean = false): string { if (skipEncoding) { // we don't cache that - return _toString(minimalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toString(_minimalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } if (!this._formatted) { - this._formatted = _toString(normalEncode, this.scheme, this.authority, this.path, this.query, this.fragment); + this._formatted = _toString(_normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } return this._formatted; } @@ -495,7 +494,7 @@ function _makeFsPath(uri: URI): string { value = uri.path; } if (isWindows) { - value = value.replace(/\//g, '\\'); + value = value.replace(_slashRegExp, '\\'); } return value; } @@ -515,12 +514,13 @@ function decodeURIComponentGraceful(str: string): string { } } -const _hex2 = /(%[0-9A-Za-z][0-9A-Za-z])+/g; +const _encodedAsHexRegExp = /(%[0-9A-Za-z][0-9A-Za-z])+/g; + function percentDecode(str: string): string { - if (!str.match(_hex2)) { + if (!str.match(_encodedAsHexRegExp)) { return str; } - return str.replace(_hex2, (match) => decodeURIComponentGraceful(match)); + return str.replace(_encodedAsHexRegExp, (match) => decodeURIComponentGraceful(match)); } //#endregion @@ -631,8 +631,10 @@ function percentEncode(str: string, mustEncode: (code: number) => boolean): stri const enum EncodePart { user, authority, path, query, fragment } -const normalEncode: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; -const minimalEncode: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; +const _normalEncoder: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; +const _minimalEncoder: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; + +const _driveLetterRegExp = /(\/?[a-z])(:|%3a)/i; /** * Create the external version of a uri @@ -645,8 +647,7 @@ function _toString(encoder: { (code: number): boolean }[], scheme: string, autho res += ':'; } if (authority || scheme === 'file') { - res += _slash; - res += _slash; + res += '//'; } if (authority) { const idxUserInfo = authority.indexOf('@'); @@ -677,8 +678,7 @@ function _toString(encoder: { (code: number): boolean }[], scheme: string, autho let pathEncoded = percentEncode(path, encoder[EncodePart.path]); // lower-case windows drive letters in /C:/fff or C:/fff and escape `:` - - let match = /(\/?[a-z])(:|%3a)/i.exec(pathEncoded); // <- todo@joh make fast! + let match = _driveLetterRegExp.exec(pathEncoded); if (match) { pathEncoded = match[1].toLowerCase() + '%3A' + pathEncoded.substr(match[0].length); } From 6f59bbd6001b496c62886ff331b43d4938083239 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 16:45:50 +0200 Subject: [PATCH 21/89] fix drive letter regexp issue --- src/vs/base/common/uri.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 2a925eabbfd..ca77c3b5525 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -634,7 +634,7 @@ const enum EncodePart { const _normalEncoder: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; const _minimalEncoder: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; -const _driveLetterRegExp = /(\/?[a-z])(:|%3a)/i; +const _driveLetterRegExp = /^(\/?[a-z])(:|%3a)/i; /** * Create the external version of a uri From 94d6dfd2a41e0f5504bcf00bc10d80f0fbe15d24 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 17:20:57 +0200 Subject: [PATCH 22/89] more test tweak... --- src/vs/base/test/node/uri.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 6901373fb5c..533392e345f 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -580,11 +580,11 @@ suite('URI', () => { 'd:/my/c#project/d.cs', 'c:\\win\\path', 'c:\\test\\drive', - '\\\\shäres\\path\\c#\\plugin.json', - '\\\\localhost\\c$\\GitDevelopment\\express', 'c:\\test with %\\path', 'c:\\test with %25\\path', 'c:\\test with %25\\c#code', + '\\\\shäres\\path\\c#\\plugin.json', + '\\\\localhost\\c$\\GitDevelopment\\express', '\\\\shares', '\\\\shares\\', ]; @@ -618,11 +618,11 @@ suite('URI', () => { 'file:///f:/fo%C3%B6/bar', 'file:///f:/', 'file:///f:/my/c%23project/c.cs', - 'file://unc-host/foö/bar', - 'file://unc-host/', 'file:///c:/bar/foo', 'file:///c:/alex.txt', 'file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', + 'file://unc-host/foö/bar', + 'file://unc-host/', 'file://monacotools/folder/isi.txt', 'file://monacotools1/certificates/SSL/', ]; From 11e2013380df797147eec0c1ba7ce7746525e179 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 23 Oct 2019 17:32:23 +0200 Subject: [PATCH 23/89] and more tweaks... --- src/vs/base/test/node/uri.test.ts | 118 ++++++++++++++---------------- 1 file changed, 55 insertions(+), 63 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 533392e345f..e4ad09abe26 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -546,7 +546,7 @@ suite('URI', () => { actual = actual.replace(/(\/[a-z])%3A/, '$1:'); } let expected = pathToFileURL(path).href; - assert.equal(actual, expected); + assert.equal(actual, expected, path); if (recurse) { assertFsPathFromUri(expected, false); } @@ -555,80 +555,72 @@ suite('URI', () => { function assertFsPathFromUri(uri: string, recurse = true): void { let actual = URI.parse(uri).fsPath; let expected = fileURLToPath(uri); - assert.equal(actual, expected); + assert.equal(actual, expected, uri); if (recurse) { assertUriFromFsPath(actual, false); } } test('URI.file and pathToFileURL', function () { - const posixPaths = [ - '/foo/bar', - '/foo/%2e.txt', - '/foo/%A0.txt', - '/foo/ü.txt', - '/foo/ß.txt', - '/my/c#project/d.cs', - '/c\\win\\path' - ]; - const windowsPaths = [ - 'd:/foo/bar', - 'd:/foo/%2e.txt', - 'd:/foo/%A0.txt', - 'd:/foo/ü.txt', - 'd:/foo/ß.txt', - 'd:/my/c#project/d.cs', - 'c:\\win\\path', - 'c:\\test\\drive', - 'c:\\test with %\\path', - 'c:\\test with %25\\path', - 'c:\\test with %25\\c#code', - '\\\\shäres\\path\\c#\\plugin.json', - '\\\\localhost\\c$\\GitDevelopment\\express', - '\\\\shares', - '\\\\shares\\', - ]; - // nodejs is strict to the platform on which it runs - (isWindows ? windowsPaths : posixPaths).forEach(p => assertUriFromFsPath(p)); + if (isWindows) { + assertUriFromFsPath('d:/foo/bar'); + assertUriFromFsPath('d:/foo/%2e.txt'); + assertUriFromFsPath('d:/foo/%A0.txt'); + assertUriFromFsPath('d:/foo/ü.txt'); + assertUriFromFsPath('d:/foo/ß.txt'); + assertUriFromFsPath('d:/my/c#project/d.cs'); + assertUriFromFsPath('c:\\win\\path'); + assertUriFromFsPath('c:\\test\\drive'); + assertUriFromFsPath('c:\\test with %\\path'); + assertUriFromFsPath('c:\\test with %25\\path'); + assertUriFromFsPath('c:\\test with %25\\c#code'); + assertUriFromFsPath('\\\\shäres\\path\\c#\\plugin.json'); + assertUriFromFsPath('\\\\localhost\\c$\\GitDevelopment\\express'); + assertUriFromFsPath('\\\\shares'); + assertUriFromFsPath('\\\\shares\\'); + } else { + assertUriFromFsPath('/foo/bar'); + assertUriFromFsPath('/foo/%2e.txt'); + assertUriFromFsPath('/foo/%A0.txt'); + assertUriFromFsPath('/foo/ü.txt'); + assertUriFromFsPath('/foo/ß.txt'); + assertUriFromFsPath('/my/c#project/d.cs'); + assertUriFromFsPath('/c\\win\\path'); + } }); test('URI.fsPath and fileURLToPath', function () { - const posixUris = [ - 'file:///foo/bar', - 'file:///fo%25/bar', - 'file:///foo/b ar/text.cs', - 'file:///foö/bar', - 'file:///fo%C3%B6/bar', - 'file:///', - 'file:///my/c%23project/c.cs', - 'file:///foo%5cbar', - 'file:///foo%5Cbar', - 'file:///foo%5C%5cbar', - 'file:///Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', - 'file:///monacotools/folder/isi.txt', - 'file:///monacotools1/certificates/SSL/', - ]; - const windowsUris = [ - 'file:///f:/foo/bar', - 'file:///f:/fo%25/bar', - 'file:///f:/foo/b ar/text.cs', - 'file:///f:/foö/bar', - 'file:///f:/fo%C3%B6/bar', - 'file:///f:/', - 'file:///f:/my/c%23project/c.cs', - 'file:///c:/bar/foo', - 'file:///c:/alex.txt', - 'file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', - 'file://unc-host/foö/bar', - 'file://unc-host/', - 'file://monacotools/folder/isi.txt', - 'file://monacotools1/certificates/SSL/', - ]; - // nodejs is strict to the platform on which it runs - (isWindows ? windowsUris : posixUris).forEach(u => assertFsPathFromUri(u)); + if (isWindows) { + assertFsPathFromUri('file:///f:/foo/bar'); + assertFsPathFromUri('file:///f:/fo%25/bar'); + assertFsPathFromUri('file:///f:/foo/b ar/text.cs'); + assertFsPathFromUri('file:///f:/foö/bar'); + assertFsPathFromUri('file:///f:/fo%C3%B6/bar'); + assertFsPathFromUri('file:///f:/'); + assertFsPathFromUri('file:///f:/my/c%23project/c.cs'); + assertFsPathFromUri('file:///c:/bar/foo'); + assertFsPathFromUri('file:///c:/alex.txt'); + assertFsPathFromUri('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins'); + assertFsPathFromUri('file://unc-host/foö/bar'); + assertFsPathFromUri('file://unc-host/'); + assertFsPathFromUri('file://monacotools/folder/isi.txt'); + assertFsPathFromUri('file://monacotools1/certificates/SSL/'); + } else { + assertFsPathFromUri('file:///foo/bar'); + assertFsPathFromUri('file:///fo%25/bar'); + assertFsPathFromUri('file:///foo/b ar/text.cs'); + assertFsPathFromUri('file:///foö/bar'); + assertFsPathFromUri('file:///fo%C3%B6/bar'); + assertFsPathFromUri('file:///'); + assertFsPathFromUri('file:///my/c%23project/c.cs'); + assertFsPathFromUri('file:///foo%5cbar'); + assertFsPathFromUri('file:///foo%5Cbar'); + assertFsPathFromUri('file:///foo%5C%5cbar'); + assertFsPathFromUri('file:///Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins'); + } }); // ---- check against standard url From df402a4042e95437bed59f0f1604456d4ac2b060 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Oct 2019 17:35:07 +0200 Subject: [PATCH 24/89] Make timeout for slow task provider 4 seconds --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index c596194095f..4dbc9e5b8d1 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1355,7 +1355,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.notificationService.prompt(Severity.Warning, nls.localize('TaskSystem.slowProvider', "The {0} task provider is slow. The extension that provides {0} tasks may provide a setting to disable it, or you can disable all tasks providers", type), [settings, disableAll, dontShow]); } - }, 2000); + }, 4000); } }); } From 563b597b9383269edb0f06f40e74062992221883 Mon Sep 17 00:00:00 2001 From: Greg Van Liew Date: Wed, 23 Oct 2019 08:41:02 -0700 Subject: [PATCH 25/89] WorkspaceConfiguration API clarifications --- src/vs/vscode.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 6a877468bf3..d528dda9b50 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4080,11 +4080,11 @@ declare module 'vscode' { * - Workspace configuration (if available) * - Workspace folder configuration of the requested resource (if available) * - * *Global configuration* comes from User Settings and shadows Defaults. + * *Global configuration* comes from User Settings and overrides Defaults. * - * *Workspace configuration* comes from Workspace Settings and shadows Global configuration. + * *Workspace configuration* comes from Workspace Settings and overrides Global configuration. * - * *Workspace Folder configuration* comes from `.vscode` folder under one of the [workspace folders](#workspace.workspaceFolders). + * *Workspace Folder configuration* comes from `.vscode` folder under one of the [workspace folders](#workspace.workspaceFolders) and overrides Workspace configuration. * * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be * part of the section identifier. The following snippets shows how to retrieve all configurations @@ -4133,9 +4133,9 @@ declare module 'vscode' { * a workspace-specific value and a folder-specific value. * * The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) - * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `workspaceFolderValue`. - * Refer to [Settings Inheritance](https://code.visualstudio.com/docs/getstarted/settings) + * is computed like this: `defaultValue` overridden by `globalValue`, + * `globalValue` overridden by `workspaceValue`. `workspaceValue` overwridden by `workspaceFolderValue`. + * Refer to [Settings](https://code.visualstudio.com/docs/getstarted/settings) * for more information. * * *Note:* The configuration name must denote a leaf in the configuration tree From 5a591cdfe7ce2c2d81602d67c06dfbb995e47a6f Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Oct 2019 09:34:53 -0700 Subject: [PATCH 26/89] Update remote explorer to use icon font --- .../ui/codiconLabel/codicon/codicon.css | 149 +++++++++--------- .../ui/codiconLabel/codicon/codicon.ttf | Bin 53184 -> 53420 bytes .../browser/parts/compositeBarActions.ts | 11 +- .../remote/browser/remote-activity-bar.svg | 13 -- .../contrib/remote/browser/remote.ts | 2 +- .../contrib/remote/browser/remoteViewlet.css | 4 - 6 files changed, 81 insertions(+), 98 deletions(-) delete mode 100644 src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index bec8e8b5783..bbc8cd8e7df 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?a131ba6056e336dfd62f50ba74386e6e") format("truetype"); + src: url("./codicon.ttf?297296ccad95f5838f73ae67efd78fbc") format("truetype"); } .codicon[class*='codicon-'] { @@ -309,76 +309,77 @@ .codicon-references:before { content: "\f1a0" } .codicon-refresh:before { content: "\f1a1" } .codicon-regex:before { content: "\f1a2" } -.codicon-remote:before { content: "\f1a3" } -.codicon-remove:before { content: "\f1a4" } -.codicon-replace-all:before { content: "\f1a5" } -.codicon-replace:before { content: "\f1a6" } -.codicon-repo-clone:before { content: "\f1a7" } -.codicon-repo-force-push:before { content: "\f1a8" } -.codicon-repo-pull:before { content: "\f1a9" } -.codicon-repo-push:before { content: "\f1aa" } -.codicon-report:before { content: "\f1ab" } -.codicon-request-changes:before { content: "\f1ac" } -.codicon-rocket:before { content: "\f1ad" } -.codicon-root-folder-opened:before { content: "\f1ae" } -.codicon-root-folder:before { content: "\f1af" } -.codicon-rss:before { content: "\f1b0" } -.codicon-ruby:before { content: "\f1b1" } -.codicon-save-all:before { content: "\f1b2" } -.codicon-save-as:before { content: "\f1b3" } -.codicon-save:before { content: "\f1b4" } -.codicon-screen-full:before { content: "\f1b5" } -.codicon-screen-normal:before { content: "\f1b6" } -.codicon-search-stop:before { content: "\f1b7" } -.codicon-selection:before { content: "\f1b8" } -.codicon-server:before { content: "\f1b9" } -.codicon-settings-gear:before { content: "\f1ba" } -.codicon-settings:before { content: "\f1bb" } -.codicon-shield:before { content: "\f1bc" } -.codicon-smiley:before { content: "\f1bd" } -.codicon-sort-precedence:before { content: "\f1be" } -.codicon-split-horizontal:before { content: "\f1bf" } -.codicon-split-vertical:before { content: "\f1c0" } -.codicon-squirrel:before { content: "\f1c1" } -.codicon-star-full:before { content: "\f1c2" } -.codicon-star-half:before { content: "\f1c3" } -.codicon-symbol-class:before { content: "\f1c4" } -.codicon-symbol-color:before { content: "\f1c5" } -.codicon-symbol-constant:before { content: "\f1c6" } -.codicon-symbol-enum-member:before { content: "\f1c7" } -.codicon-symbol-field:before { content: "\f1c8" } -.codicon-symbol-file:before { content: "\f1c9" } -.codicon-symbol-interface:before { content: "\f1ca" } -.codicon-symbol-keyword:before { content: "\f1cb" } -.codicon-symbol-misc:before { content: "\f1cc" } -.codicon-symbol-operator:before { content: "\f1cd" } -.codicon-symbol-property:before { content: "\f1ce" } -.codicon-symbol-snippet:before { content: "\f1cf" } -.codicon-tasklist:before { content: "\f1d0" } -.codicon-telescope:before { content: "\f1d1" } -.codicon-text-size:before { content: "\f1d2" } -.codicon-three-bars:before { content: "\f1d3" } -.codicon-thumbsdown:before { content: "\f1d4" } -.codicon-thumbsup:before { content: "\f1d5" } -.codicon-tools:before { content: "\f1d6" } -.codicon-triangle-down:before { content: "\f1d7" } -.codicon-triangle-left:before { content: "\f1d8" } -.codicon-triangle-right:before { content: "\f1d9" } -.codicon-triangle-up:before { content: "\f1da" } -.codicon-twitter:before { content: "\f1db" } -.codicon-unfold:before { content: "\f1dc" } -.codicon-unlock:before { content: "\f1dd" } -.codicon-unmute:before { content: "\f1de" } -.codicon-unverified:before { content: "\f1df" } -.codicon-verified:before { content: "\f1e0" } -.codicon-versions:before { content: "\f1e1" } -.codicon-vm-active:before { content: "\f1e2" } -.codicon-vm-outline:before { content: "\f1e3" } -.codicon-vm-running:before { content: "\f1e4" } -.codicon-watch:before { content: "\f1e5" } -.codicon-whitespace:before { content: "\f1e6" } -.codicon-whole-word:before { content: "\f1e7" } -.codicon-window:before { content: "\f1e8" } -.codicon-word-wrap:before { content: "\f1e9" } -.codicon-zoom-in:before { content: "\f1ea" } -.codicon-zoom-out:before { content: "\f1eb" } +.codicon-remote-explorer:before { content: "\f1a3" } +.codicon-remote:before { content: "\f1a4" } +.codicon-remove:before { content: "\f1a5" } +.codicon-replace-all:before { content: "\f1a6" } +.codicon-replace:before { content: "\f1a7" } +.codicon-repo-clone:before { content: "\f1a8" } +.codicon-repo-force-push:before { content: "\f1a9" } +.codicon-repo-pull:before { content: "\f1aa" } +.codicon-repo-push:before { content: "\f1ab" } +.codicon-report:before { content: "\f1ac" } +.codicon-request-changes:before { content: "\f1ad" } +.codicon-rocket:before { content: "\f1ae" } +.codicon-root-folder-opened:before { content: "\f1af" } +.codicon-root-folder:before { content: "\f1b0" } +.codicon-rss:before { content: "\f1b1" } +.codicon-ruby:before { content: "\f1b2" } +.codicon-save-all:before { content: "\f1b3" } +.codicon-save-as:before { content: "\f1b4" } +.codicon-save:before { content: "\f1b5" } +.codicon-screen-full:before { content: "\f1b6" } +.codicon-screen-normal:before { content: "\f1b7" } +.codicon-search-stop:before { content: "\f1b8" } +.codicon-selection:before { content: "\f1b9" } +.codicon-server:before { content: "\f1ba" } +.codicon-settings-gear:before { content: "\f1bb" } +.codicon-settings:before { content: "\f1bc" } +.codicon-shield:before { content: "\f1bd" } +.codicon-smiley:before { content: "\f1be" } +.codicon-sort-precedence:before { content: "\f1bf" } +.codicon-split-horizontal:before { content: "\f1c0" } +.codicon-split-vertical:before { content: "\f1c1" } +.codicon-squirrel:before { content: "\f1c2" } +.codicon-star-full:before { content: "\f1c3" } +.codicon-star-half:before { content: "\f1c4" } +.codicon-symbol-class:before { content: "\f1c5" } +.codicon-symbol-color:before { content: "\f1c6" } +.codicon-symbol-constant:before { content: "\f1c7" } +.codicon-symbol-enum-member:before { content: "\f1c8" } +.codicon-symbol-field:before { content: "\f1c9" } +.codicon-symbol-file:before { content: "\f1ca" } +.codicon-symbol-interface:before { content: "\f1cb" } +.codicon-symbol-keyword:before { content: "\f1cc" } +.codicon-symbol-misc:before { content: "\f1cd" } +.codicon-symbol-operator:before { content: "\f1ce" } +.codicon-symbol-property:before { content: "\f1cf" } +.codicon-symbol-snippet:before { content: "\f1d0" } +.codicon-tasklist:before { content: "\f1d1" } +.codicon-telescope:before { content: "\f1d2" } +.codicon-text-size:before { content: "\f1d3" } +.codicon-three-bars:before { content: "\f1d4" } +.codicon-thumbsdown:before { content: "\f1d5" } +.codicon-thumbsup:before { content: "\f1d6" } +.codicon-tools:before { content: "\f1d7" } +.codicon-triangle-down:before { content: "\f1d8" } +.codicon-triangle-left:before { content: "\f1d9" } +.codicon-triangle-right:before { content: "\f1da" } +.codicon-triangle-up:before { content: "\f1db" } +.codicon-twitter:before { content: "\f1dc" } +.codicon-unfold:before { content: "\f1dd" } +.codicon-unlock:before { content: "\f1de" } +.codicon-unmute:before { content: "\f1df" } +.codicon-unverified:before { content: "\f1e0" } +.codicon-verified:before { content: "\f1e1" } +.codicon-versions:before { content: "\f1e2" } +.codicon-vm-active:before { content: "\f1e3" } +.codicon-vm-outline:before { content: "\f1e4" } +.codicon-vm-running:before { content: "\f1e5" } +.codicon-watch:before { content: "\f1e6" } +.codicon-whitespace:before { content: "\f1e7" } +.codicon-whole-word:before { content: "\f1e8" } +.codicon-window:before { content: "\f1e9" } +.codicon-word-wrap:before { content: "\f1ea" } +.codicon-zoom-in:before { content: "\f1eb" } +.codicon-zoom-out:before { content: "\f1ec" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index fe514c9c37046d5abf07b0caa2c819900f629f51..2f278d4e0bf18522e12cde8e79fcfaadaf35dc99 100644 GIT binary patch delta 1540 zcmXw(eN0nV7{;G-iv4|L8sv#IkBXBV?gGQX$d7n{iD$CRm4yhCQEPUc6ux18)IzkBX|&wI|zd7dZt z@Tao9pUF1pTGyA91FQqUylS`0llp2{Ga#=4qO%s)uXsa0@8DA)?l$m6dR?unM(JI# zk$MM+j5;cWCWOqT=Wb$hox8ck6&LW+e$*eby1ud6_4r^9*@ z_s7ToKL!GU9N9gYk>0}~1gmt>8m1LB?CbC$S$d!dl{A0Idxe=~Vo!*}>t_lkR>>!% z?HT<7gq@I$KBds80Aza*BDByxiqSm4AmL98;V(RZ4-fGePw+3EF~$TYVUSb-SK*Gtz z=&>WBc5HZ5L{Q6d8-n*yLDh<+ai;*Onu8OfLy_1TG)Y>_Ax$9Fae_&amT*WXNJ}}S z7^F8j$e^*F6HJEW=8&F{8aSjZq(%;D49UYGwIRI)@t&fD1w>PL5>tr_>3&pP9iGO* z#C_rBmxrlkEr;ZW)XpK{A+6)!t~fqI<$6SKA92VINObBTWDF#VeGsw;(k2d>1xe(P zZICu|$UsORbI3|aog6Z?J>p{MD@Op|E)LlX-);^W4c{IPSq|S<9Gnz8Vz#hLq9N8C zca>^CaL8`>4smcpTos$dZi(AtRqT%VZEPAF5(i_GvVAmvmO~MM?;MB10N;5I#R9$y z90~}0y&OCf9m)!d$X4Z-OeyB73L<<%G(`k6;_Xnq!&*m-6O9JrNJc|OXXd2Ll_tT| zWa^!fGNWq7)+~8eLsq{z!kl4VZa!`vwoI|qSbDR=vL|P6wyLc))TRiG?2&4i;@Mx>@w7SYE6yPA={!?kiE0EGii+%_{Yj z9w?KSm6a_o>na;4SC@ZSzHd(ZoW6>QbegkqQaik_m6 z)uH~(6HE;NFh_7sR~u;iA=poR-)4+J%7mZ zBz6FjJM6QJ+;~5D=USoT7Q>v zJWF_SK%3w+=j7;w_5QB;o3?l~mNlj6RZfH7WOpyCP^T`kOmSpt)-0gK)+{TnUEqE4 rev_4Hy-SZ+1wqhxcO4rJ9Us+H>uzkWom$)Csc&qmZSr=WF@*dFOxD$v delta 1343 zcmXw(eNfYN9LL|^ZEiRol#8b+L4sFaN0A-Qxs8Ey447j~Bqh>wt(nid&;N$h7@=F!#%zSr(vzwdY7-{<%F-M!wQ zt?LtI-^a>5%j%n5Zh*A_c&enr;|(v%Dg@Ne1Cc3P%3pn9w%j!V=thChyk(`H%`u0! zU#HruguaXpG>NK>wC^F7msQl%KC?0ypnj8pYDalhiD&=m;rD@9>aWPG@YH%0MqHr# zaa8|MrKh4aep%<*H-MvND$shXUa2XHc#rn|IY7j=y*de~}rK(wHj8A7E-G zZB{>!+A;qIDB7W%-=)y11(Y37DYjD|?R-xK-^p8en>;NYR6kNOj6rO}*9f8uKVd(9 zfu6-;9GCDTp2iA%i9$4@4XFqq7vU@hrT7T_=)|Y+F%9ZbiCP$N6GiwJ*U^kW@f+@9 z7Joy=Yxo`i_@B_^G0yP1|}6$<7W ze#Rtz#T0IUV;VEKg+Fi`ckmbP;XVUnOu>{)#TJl(5a~+D(#SZbV@udl7SA4J38YOe zMJ^0TeQbfWNUL7hIInYFrZzcz7~mxW3`#C-QaxD!-y$F%;AH|b1HM&2j=*0OkS*|+ z1klr3E+B*86#{YzUMV1};8gPK(d4;vlwX;UHhe`&{U?8xu=p0jUPxDIn?K%>vR7{*Hix0HG47^o9(Sb_>3K4uC zkT7?lJi*@;P_p1{0?K$-*jdfW0YL5$P}-0?1(ZAFLjsxr$X^IBBDF?+$R?!6qfNTY zbk-}N^dTP=;Hvaybh3_qhVooTf+hm;pa4@+Z*(e~k!GTk({Iz+sDNe!@(BS=3*<2Y z%?;#}0^Fzdlz>5U#cZS*YLEGx#Yv7>hrd1cbyj~QZD>`SK1`pc|7LaE>H~(whDyV6 zLtu?@jn5cntT7%ko-;+6Hkn#XgJy-f(tOZ7kp5(Pw?$!TvJ7RYGj?Q*WEN-EWgb{N zVpUs{tZr+Kwaa>Eop0TkZLzJ$)?oX}b}1`9Ygg8&U12Y<*JUrsF3vua9mp}~^yJLi za_zYvzkYwXM?lV>2r2FNAe!ctIq3i!PVqCpC6H5nD5J= 0; - const isRemoteExplorer = this.label.className.indexOf('remote') >= 0; - if (!isRemoteExplorer && !isExtension) { + const isExtension = this.activity.cssClass?.indexOf('extensionViewlet') === 0; + if (!isExtension) { // Apply foreground color to activity bar items (codicons) this.label.style.color = foreground ? foreground.toString() : ''; } else { // Apply background color to extensions + remote explorer (svgs) + this.label.style.backgroundColor = foreground ? foreground.toString() : ''; } } else { @@ -318,9 +318,8 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.label.className = 'action-label'; if (this.activity.cssClass) { - const isExtension = this.activity.cssClass.indexOf('extensionViewlet-') >= 0; - const isRemoteExplorer = this.activity.cssClass.indexOf('remote') >= 0; - if (this.options.icon && !isRemoteExplorer && !isExtension) { + const isExtension = this.activity.cssClass?.indexOf('extensionViewlet') === 0; + if (this.options.icon && !isExtension) { // Only apply icon class to activity bar items (exclude extensions + remote explorer) dom.addClass(this.label, 'codicon'); } diff --git a/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg b/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg deleted file mode 100644 index 029e6b051c2..00000000000 --- a/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index bc4db1d2a9f..9da7cb04259 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -461,7 +461,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie RemoteViewlet, VIEWLET_ID, nls.localize('remote.explorer', "Remote Explorer"), - 'remote', + 'codicon-remote-explorer', 4 )); diff --git a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css index 86bd4b76dc9..3d83e98d7ce 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css +++ b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css @@ -3,10 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .activitybar>.content .monaco-action-bar .action-label.remote { - -webkit-mask: url('remote-activity-bar.svg') no-repeat 50% 50%; -} - .remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item { display: flex; height: 22px; From 3e1c84cdec2f4eec0f3ef7b6bc669f8c25bbbc1d Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Oct 2019 09:38:34 -0700 Subject: [PATCH 27/89] Add todo --- src/vs/workbench/browser/parts/compositeBarActions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 9fbfee9c6b4..cfcbbf171b1 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -158,6 +158,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { if (this.label) { if (this.options.icon) { const foreground = this._action.checked ? colors.activeBackgroundColor || colors.activeForegroundColor : colors.inactiveBackgroundColor || colors.inactiveForegroundColor; + // TODO @misolori find a cleaner way to do this const isExtension = this.activity.cssClass?.indexOf('extensionViewlet') === 0; if (!isExtension) { // Apply foreground color to activity bar items (codicons) @@ -318,6 +319,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { this.label.className = 'action-label'; if (this.activity.cssClass) { + // TODO @misolori find a cleaner way to do this const isExtension = this.activity.cssClass?.indexOf('extensionViewlet') === 0; if (this.options.icon && !isExtension) { // Only apply icon class to activity bar items (exclude extensions + remote explorer) From f554449e3baa6db81c04ed2c321c60ec3a8e9b9f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Oct 2019 18:40:06 +0200 Subject: [PATCH 28/89] fixes #83167 --- src/vs/vscode.d.ts | 35 +++++++++++++++++++++++++++++++++++ src/vs/vscode.proposed.d.ts | 35 ----------------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d528dda9b50..27b2d1c77f4 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -9282,6 +9282,41 @@ declare module 'vscode' { constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); } + /** + * Debug console mode used by debug session, see [options](#DebugSessionOptions). + */ + export enum DebugConsoleMode { + /** + * Debug session should have a separate debug console. + */ + Separate = 0, + + /** + * Debug session should share debug console with its parent session. + * This value has no effect for sessions which do not have a parent session. + */ + MergeWithParent = 1 + } + + /** + * Options for [starting a debug session](#debug.startDebugging). + */ + export interface DebugSessionOptions { + + /** + * When specified the newly created debug session is registered as a "child" session of this + * "parent" debug session. + */ + parentSession?: DebugSession; + + /** + * Controls whether this session should have a separate debug console or share it + * with the parent session. Has no effect for sessions which do not have a parent session. + * Defaults to Separate. + */ + consoleMode?: DebugConsoleMode; + } + /** * Namespace for debug functionality. */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 535adbb5c02..6f6c6f7db47 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -616,41 +616,6 @@ declare module 'vscode' { debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; } - /** - * Debug console mode used by debug session, see [options](#DebugSessionOptions). - */ - export enum DebugConsoleMode { - /** - * Debug session should have a separate debug console. - */ - Separate = 0, - - /** - * Debug session should share debug console with its parent session. - * This value has no effect for sessions which do not have a parent session. - */ - MergeWithParent = 1 - } - - /** - * Options for [starting a debug session](#debug.startDebugging). - */ - export interface DebugSessionOptions { - - /** - * When specified the newly created debug session is registered as a "child" session of this - * "parent" debug session. - */ - parentSession?: DebugSession; - - /** - * Controls whether this session should have a separate debug console or share it - * with the parent session. Has no effect for sessions which do not have a parent session. - * Defaults to Separate. - */ - consoleMode?: DebugConsoleMode; - } - //#endregion //#region Rob, Matt: logging From 844335b389f8e3a2d4410700777b23e5db3fe459 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 23 Oct 2019 13:32:37 -0400 Subject: [PATCH 29/89] Closes #54507 - Adds themable window border (#82781) --- src/vs/workbench/browser/layout.ts | 81 ++++++++++++++++++++++-- src/vs/workbench/browser/media/style.css | 13 ++++ src/vs/workbench/common/theme.ts | 12 ++++ 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 84c9cab73a4..49ccfa95402 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, position, size, Dimension } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, Dimension, toggleClass } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -38,6 +38,8 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { coalesce } from 'vs/base/common/arrays'; import { assertIsDefined } from 'vs/base/common/types'; import { INotificationService, NotificationsFilter } from 'vs/platform/notification/common/notification'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from 'vs/workbench/common/theme'; enum Settings { ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', @@ -74,7 +76,8 @@ enum Classes { EDITOR_HIDDEN = 'noeditorarea', PANEL_HIDDEN = 'nopanel', STATUSBAR_HIDDEN = 'nostatusbar', - FULLSCREEN = 'fullscreen' + FULLSCREEN = 'fullscreen', + WINDOW_BORDER = 'border' } export abstract class Layout extends Disposable implements IWorkbenchLayoutService { @@ -135,9 +138,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private contextService!: IWorkspaceContextService; private backupFileService!: IBackupFileService; private notificationService!: INotificationService; + private themeService!: IThemeService; protected readonly state = { fullscreen: false, + hasFocus: false, + windowBorder: false, menuBar: { visibility: 'default' as MenuBarVisibility, @@ -204,6 +210,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.contextService = accessor.get(IWorkspaceContextService); this.storageService = accessor.get(IStorageService); this.backupFileService = accessor.get(IBackupFileService); + this.themeService = accessor.get(IThemeService); // Parts this.editorService = accessor.get(IEditorService); @@ -257,6 +264,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } + + // Theme changes + this._register(this.themeService.onThemeChange(theme => this.updateStyles())); + + // Window focus changes + this._register(this.hostService.onDidChangeFocus(e => this.onWindowFocusChanged(e))); } private onMenubarToggled(visible: boolean) { @@ -297,6 +310,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._onFullscreenChange.fire(this.state.fullscreen); } + private onWindowFocusChanged(hasFocus: boolean): void { + if (this.state.hasFocus === hasFocus) { + return; + } + + this.state.hasFocus = hasFocus; + this.updateWindowBorder(); + } + private doUpdateLayoutConfiguration(skipLayout?: boolean): void { // Sidebar position @@ -366,6 +388,44 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.layout(); } + private updateWindowBorder(skipLayout: boolean = false) { + if (isWeb || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') { + return; + } + + const theme = this.themeService.getTheme(); + + const activeBorder = theme.getColor(WINDOW_ACTIVE_BORDER); + const inactiveBorder = theme.getColor(WINDOW_INACTIVE_BORDER); + + let windowBorder = false; + if (activeBorder || inactiveBorder) { + windowBorder = true; + + // If one color is missing, just fallback to the other one + const borderColor = this.state.hasFocus + ? activeBorder ?? inactiveBorder + : inactiveBorder ?? activeBorder; + this.container.style.setProperty('--window-border-color', borderColor ? borderColor.toString() : 'transparent'); + } + + if (windowBorder === this.state.windowBorder) { + return; + } + + this.state.windowBorder = windowBorder; + + toggleClass(this.container, Classes.WINDOW_BORDER, windowBorder); + + if (!skipLayout) { + this.layout(); + } + } + + private updateStyles() { + this.updateWindowBorder(); + } + private initLayoutState(lifecycleService: ILifecycleService, fileService: IFileService): void { // Fullscreen @@ -442,6 +502,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Zen mode enablement this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + this.state.hasFocus = this.hostService.hasFocus; + + // Window border + this.updateWindowBorder(true); + } private resolveEditorsToOpen(fileService: IFileService): Promise | IResourceEditor[] { @@ -826,12 +891,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi })); } + getClientArea(): Dimension { + const dim = getClientArea(this.parent); + return this.state.windowBorder ? new Dimension(dim.width - 2, dim.height - 2) : dim; + } + layout(): void { if (!this.disposed) { - this._dimension = getClientArea(this.parent); - - position(this.container, 0, 0, 0, 0, 'relative'); - size(this.container, this._dimension.width, this._dimension.height); + this._dimension = this.getClientArea(); // Layout the grid widget this.workbenchGrid.layout(this._dimension.width, this._dimension.height); @@ -1169,7 +1236,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private createGridDescriptor(): ISerializedGrid { - const workbenchDimensions = getClientArea(this.parent); + const workbenchDimensions = this.getClientArea(); const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width); const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height); // At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 2aa9b8cbcec..4218aec3c9f 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -51,6 +51,19 @@ body.web { position: relative; z-index: 1; overflow: hidden; + height: 100vh; + width: 100vw; +} + +.monaco-workbench.border { + position: relative; + height: calc(100vh - 2px); + width: calc(100vw - 2px); + border: 1px solid var(--window-border-color); +} + +.monaco-workbench.border.mac { + border-radius: 5px; } .monaco-workbench img { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index ec9755085ca..0f7f3766a11 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -589,6 +589,18 @@ export const NOTIFICATIONS_INFO_ICON_FOREGROUND = registerColor('notificationsIn hc: editorInfoForeground }, nls.localize('notificationsInfoIconForeground', "The color used for the notification info icon.")); +export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', { + dark: null, + light: null, + hc: contrastBorder +}, nls.localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the desktop client.")); + +export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', { + dark: null, + light: null, + hc: contrastBorder +}, nls.localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client.")); + /** * Base class for all themable workbench components. */ From 064bc15f0dcbd17e1811d0798a38ca9604c788e1 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Oct 2019 11:04:21 -0700 Subject: [PATCH 30/89] Really fix #64684 --- .../browser/parts/editor/media/tabstitlecontrol.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 0c97c633443..ac5e650dbd3 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -199,8 +199,8 @@ } /* change close icon to dirty state icon */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label:not(:hover)::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label:not(:hover)::before { +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty:not(.dirty-border-top) > .tab-close .action-label:not(:hover)::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty:not(.dirty-border-top) > .tab-close .action-label:not(:hover)::before { content: "\ea71"; /* use `circle-filled` icon unicode */ } @@ -232,6 +232,7 @@ padding-right: 5px; /* we need less room when sizing is shrink */ } +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty-border-top > .tab-close, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty-border-top > .tab-close { display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without close button */ } From 33314439b26610be8424e087f068cd09302764cd Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 23 Oct 2019 11:16:39 -0700 Subject: [PATCH 31/89] rename browserworkbenchenvconopts --- .../services/environment/browser/environmentService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 7bcf01a7bb8..996e9ab1b19 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -21,7 +21,7 @@ import { memoize } from 'vs/base/common/decorators'; export class BrowserWindowConfiguration implements IWindowConfiguration { - constructor(private readonly options: IBrowserWorkbenchEnvironemntConstructionOptions, private readonly environment: IWorkbenchEnvironmentService) { } + constructor(private readonly options: IBrowserWorkbenchEnvironmentConstructionOptions, private readonly environment: IWorkbenchEnvironmentService) { } //#region PROPERLY CONFIGURED @@ -84,7 +84,7 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { } } -interface IBrowserWorkbenchEnvironemntConstructionOptions extends IWorkbenchConstructionOptions { +interface IBrowserWorkbenchEnvironmentConstructionOptions extends IWorkbenchConstructionOptions { workspaceId: string; logsPath: URI; } @@ -232,7 +232,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment //#endregion - constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) { } + constructor(readonly options: IBrowserWorkbenchEnvironmentConstructionOptions) { } private resolveExtensionHostDebugEnvironment(): IExtensionHostDebugEnvironment { const extensionHostDebugEnvironment: IExtensionHostDebugEnvironment = { From 5353d8765d5103cadcf8f4d9f044ea751de982c1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 11:20:14 -0700 Subject: [PATCH 32/89] xterm-addon-search@0.3.0-beta10 Fixes #62348 --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index d59dc09a3eb..bc1c0f8a0d3 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", "xterm": "^4.2.0-beta20", - "xterm-addon-search": "0.3.0-beta5", + "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/package.json b/remote/package.json index 61022cb0b94..3176c6c8a4e 100644 --- a/remote/package.json +++ b/remote/package.json @@ -21,7 +21,7 @@ "vscode-ripgrep": "^1.5.7", "vscode-textmate": "^4.2.2", "xterm": "^4.2.0-beta20", - "xterm-addon-search": "0.3.0-beta5", + "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/web/package.json b/remote/web/package.json index cf40cc22b1b..b4616e79dee 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -6,7 +6,7 @@ "semver-umd": "^5.5.3", "vscode-textmate": "^4.2.2", "xterm": "^4.2.0-beta20", - "xterm-addon-search": "0.3.0-beta5", + "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index f3ca259409b..dba49700278 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -31,10 +31,10 @@ vscode-textmate@^4.2.2: dependencies: oniguruma "^7.2.0" -xterm-addon-search@0.3.0-beta5: - version "0.3.0-beta5" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" - integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== +xterm-addon-search@0.3.0-beta10: + version "0.3.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta10.tgz#24ca32062e8a2488e7df5d2e30855f0e2ce3e23d" + integrity sha512-3yPcHDgUYBM7Co5KGJkdOPUsRj2i+ZfhLSAJrHTNEP2vK6x6dO3YJ6M+YfBfiqP6ATXBXa+xTi0h0AV/IMukvQ== xterm-addon-web-links@0.2.1: version "0.2.1" diff --git a/remote/yarn.lock b/remote/yarn.lock index 325a7be1ce4..fb9c11b0437 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -411,10 +411,10 @@ vscode-windows-registry@1.0.2: resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a" integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA== -xterm-addon-search@0.3.0-beta5: - version "0.3.0-beta5" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" - integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== +xterm-addon-search@0.3.0-beta10: + version "0.3.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta10.tgz#24ca32062e8a2488e7df5d2e30855f0e2ce3e23d" + integrity sha512-3yPcHDgUYBM7Co5KGJkdOPUsRj2i+ZfhLSAJrHTNEP2vK6x6dO3YJ6M+YfBfiqP6ATXBXa+xTi0h0AV/IMukvQ== xterm-addon-web-links@0.2.1: version "0.2.1" diff --git a/yarn.lock b/yarn.lock index d261a6fbdef..9efbb097fb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9266,10 +9266,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.3.0-beta5: - version "0.3.0-beta5" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta5.tgz#fd53d33a77a0235018479c712be8c12f7c0d083a" - integrity sha512-3GkGc4hST35/4hzgnQPLLvQ29WH7MkZ0mUrBE/Vm1IQum7TnMvWPTkGemwM+wAl4tdBmynNccHJlFeQzaQtVUg== +xterm-addon-search@0.3.0-beta10: + version "0.3.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0-beta10.tgz#24ca32062e8a2488e7df5d2e30855f0e2ce3e23d" + integrity sha512-3yPcHDgUYBM7Co5KGJkdOPUsRj2i+ZfhLSAJrHTNEP2vK6x6dO3YJ6M+YfBfiqP6ATXBXa+xTi0h0AV/IMukvQ== xterm-addon-web-links@0.2.1: version "0.2.1" From e3c63a67dade0d31625eaff4a1d87e6afd6b4daa Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 11:24:42 -0700 Subject: [PATCH 33/89] xterm@4.2.0-beta20 Diff: https://github.com/xtermjs/xterm.js/compare/6311076...d9f58c0 - Tests - Fix lgtm alerts - Properly export viewport range in API - Warn on unattached open, don't throw. Fixes #83016 --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- src/typings/xterm.d.ts | 2 +- yarn.lock | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index bc1c0f8a0d3..49aa4302da5 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", - "xterm": "^4.2.0-beta20", + "xterm": "^4.2.0-beta24", "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1", "yauzl": "^2.9.2", diff --git a/remote/package.json b/remote/package.json index 3176c6c8a4e..b8794c33e2c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -20,7 +20,7 @@ "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.7", "vscode-textmate": "^4.2.2", - "xterm": "^4.2.0-beta20", + "xterm": "^4.2.0-beta24", "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1", "yauzl": "^2.9.2", diff --git a/remote/web/package.json b/remote/web/package.json index b4616e79dee..9ef022cb6af 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,7 +5,7 @@ "onigasm-umd": "^2.2.2", "semver-umd": "^5.5.3", "vscode-textmate": "^4.2.2", - "xterm": "^4.2.0-beta20", + "xterm": "^4.2.0-beta24", "xterm-addon-search": "0.3.0-beta10", "xterm-addon-web-links": "0.2.1" } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index dba49700278..5aa453fa16e 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -41,7 +41,7 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm@^4.2.0-beta20: - version "4.2.0-beta20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta20.tgz#74a759414511b4577159293397914ac33a2375d8" - integrity sha512-lH52ksaNQqWmLVV4OdLKWvhGkSRUXgJNvb2YYmVkBAm1PdVVS36ir8Qr4zYygnc2tBw689Wj65t4cNckmfpU1Q== +xterm@^4.2.0-beta24: + version "4.2.0-beta24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta24.tgz#3f68ec50d9ae1ec2e0ee1e35f1e9f25c4060cc07" + integrity sha512-71qwhH3uo2V68QX/hEgjpn6wZ/XU1N9DzhbTBUVT0z1pJsCz9JUeDqIxenIIqkRut/MlzxUDU7mw/pX+ZVFCfg== diff --git a/remote/yarn.lock b/remote/yarn.lock index fb9c11b0437..481f8a03493 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -421,10 +421,10 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm@^4.2.0-beta20: - version "4.2.0-beta20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta20.tgz#74a759414511b4577159293397914ac33a2375d8" - integrity sha512-lH52ksaNQqWmLVV4OdLKWvhGkSRUXgJNvb2YYmVkBAm1PdVVS36ir8Qr4zYygnc2tBw689Wj65t4cNckmfpU1Q== +xterm@^4.2.0-beta24: + version "4.2.0-beta24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta24.tgz#3f68ec50d9ae1ec2e0ee1e35f1e9f25c4060cc07" + integrity sha512-71qwhH3uo2V68QX/hEgjpn6wZ/XU1N9DzhbTBUVT0z1pJsCz9JUeDqIxenIIqkRut/MlzxUDU7mw/pX+ZVFCfg== yauzl@^2.9.2: version "2.10.0" diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index e163c256726..4fd681c5bf7 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -860,7 +860,7 @@ declare module 'xterm' { /** * An object representing a range within the viewport of the terminal. */ - interface IViewportRange { + export interface IViewportRange { /** * The start cell of the range. */ diff --git a/yarn.lock b/yarn.lock index 9efbb097fb7..adf1b8b6304 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9276,10 +9276,10 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm@^4.2.0-beta20: - version "4.2.0-beta20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta20.tgz#74a759414511b4577159293397914ac33a2375d8" - integrity sha512-lH52ksaNQqWmLVV4OdLKWvhGkSRUXgJNvb2YYmVkBAm1PdVVS36ir8Qr4zYygnc2tBw689Wj65t4cNckmfpU1Q== +xterm@^4.2.0-beta24: + version "4.2.0-beta24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0-beta24.tgz#3f68ec50d9ae1ec2e0ee1e35f1e9f25c4060cc07" + integrity sha512-71qwhH3uo2V68QX/hEgjpn6wZ/XU1N9DzhbTBUVT0z1pJsCz9JUeDqIxenIIqkRut/MlzxUDU7mw/pX+ZVFCfg== y18n@^3.2.1: version "3.2.1" From 235f8512433e42f7cea9d2befa565c026bea592f Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Oct 2019 11:26:24 -0700 Subject: [PATCH 34/89] Fix #83138, hide chevrons in breadcrumb when no tabs visible and last item --- .../browser/parts/editor/media/notabstitlecontrol.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 8bc05e970dd..87db7670dab 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -76,6 +76,11 @@ padding-right: 4px; /* does not have trailing separator*/ } +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { + display: none; /* hides chevrons when no tabs visible and when last items */ +} + /* Title Actions */ .monaco-workbench .part.editor > .content .editor-group-container > .title .title-actions { display: flex; From e9593263f6b6a0721259f712bbdb61c12211d043 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 23 Oct 2019 20:29:49 +0200 Subject: [PATCH 35/89] Fixes #49428: Allow fontSize to go all the way down to 6 --- src/vs/editor/common/config/editorOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 66d3f8c7eae..07a97988670 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1258,7 +1258,7 @@ class EditorFontSize extends SimpleEditorOption { if (r === 0) { return EDITOR_FONT_DEFAULTS.fontSize; } - return EditorFloatOption.clamp(r, 8, 100); + return EditorFloatOption.clamp(r, 6, 100); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: number): number { // The final fontSize respects the editor zoom level. From 0c4f3f9cf499890d3613d3374cf778d67d5e6180 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 11:29:58 -0700 Subject: [PATCH 36/89] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49aa4302da5..1800d19ef9d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.40.0", - "distro": "e8c2fa7c1c2249304102104d56e7cacb37ca27ec", + "distro": "9409a98a9808db59c1ed02f354c4f3d5bba662ea", "author": { "name": "Microsoft Corporation" }, From 1592250db63078c1149d01b5ba2b2db2934c8696 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Oct 2019 11:37:55 -0700 Subject: [PATCH 37/89] Adjust padding for breadcrumb symbols in no tab view --- .../browser/parts/editor/media/notabstitlecontrol.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 87db7670dab..77a7fa03e7a 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -76,7 +76,11 @@ padding-right: 4px; /* does not have trailing separator*/ } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon, +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon[class*='codicon-symbol-'] { + padding: 0 1px; +} + +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon:last-child, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { display: none; /* hides chevrons when no tabs visible and when last items */ } From 2d9dbafda6bfc6ab3b702989ac52f3d67a1b2d10 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 23 Oct 2019 11:37:31 -0700 Subject: [PATCH 38/89] Fix #81958. Focus border for toggle. --- src/vs/editor/contrib/find/findWidget.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 46c8e404cae..0cc3472b692 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -139,7 +139,6 @@ } .monaco-editor .find-widget .button { - min-width: 20px; width: 20px; height: 20px; display: flex; @@ -172,7 +171,7 @@ .monaco-editor .find-widget .button.toggle { position: absolute; top: 0; - left: 0; + left: 3px; width: 18px; height: 100%; -webkit-box-sizing: border-box; From 76f7c4d6d343f8a402258b70f256c941f6f1a4f6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 23 Oct 2019 19:56:30 +0100 Subject: [PATCH 39/89] GitHub Actions: include release branches for PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: João Moreno --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 985007e1f52..63c9288ae51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - master + - release/* jobs: linux: From de010f538c3ea6f6db4067e099926786bb82beb3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 11:56:19 -0700 Subject: [PATCH 40/89] Only use endpoint origin when rewriting csp in webviews --- src/vs/workbench/contrib/webview/browser/pre/main.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 3585d8b83eb..e137096b81a 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -308,7 +308,12 @@ } else { // Rewrite vscode-resource in csp if (data.endpoint) { - csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:/g, data.endpoint)); + try { + const endpointUrl = new URL(data.endpoint); + csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:(?=(\s|;|$))/g, endpointUrl.origin)); + } catch (e) { + console.error('Could not rewrite csp'); + } } } From 66058c15527a0a35b68c75df99d04f68a5cf67a9 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 23 Oct 2019 19:56:47 +0100 Subject: [PATCH 41/89] GitHub Actions: include release branches on push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: João Moreno --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63c9288ae51..55415410f7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release/* pull_request: branches: - master From 215fa33710a01561c1a1db0f6cc339c1a9e6528a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 12:45:15 -0700 Subject: [PATCH 42/89] Support command tracking from current scroll position Fixes #46729 --- .../browser/addons/commandTrackerAddon.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts b/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts index 3c4069967ba..1911f12ef4c 100644 --- a/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/addons/commandTrackerAddon.ts @@ -64,7 +64,18 @@ export class CommandTrackerAddon implements ICommandTracker, ITerminalAddon { } let markerIndex; - if (this._currentMarker === Boundary.Bottom) { + const currentLineY = Math.min(this._getLine(this._terminal, this._currentMarker), this._terminal.buffer.baseY); + const viewportY = this._terminal.buffer.viewportY; + if (!retainSelection && currentLineY !== viewportY) { + // The user has scrolled, find the line based on the current scroll position. This only + // works when not retaining selection + const markersBelowViewport = this._terminal.markers.filter(e => e.line >= viewportY).length; + if (markersBelowViewport === 0) { + markerIndex = -1; + } else { + markerIndex = this._terminal.markers.length - markersBelowViewport - 1; + } + } else if (this._currentMarker === Boundary.Bottom) { markerIndex = this._terminal.markers.length - 1; } else if (this._currentMarker === Boundary.Top) { markerIndex = -1; @@ -95,7 +106,18 @@ export class CommandTrackerAddon implements ICommandTracker, ITerminalAddon { } let markerIndex; - if (this._currentMarker === Boundary.Bottom) { + const currentLineY = Math.min(this._getLine(this._terminal, this._currentMarker), this._terminal.buffer.baseY); + const viewportY = this._terminal.buffer.viewportY; + if (!retainSelection && currentLineY !== viewportY) { + // The user has scrolled, find the line based on the current scroll position. This only + // works when not retaining selection + const markersAboveViewport = this._terminal.markers.filter(e => e.line <= viewportY).length; + if (markersAboveViewport < this._terminal.markers.length) { + markerIndex = markersAboveViewport; + } else { + markerIndex = this._terminal.markers.length; + } + } else if (this._currentMarker === Boundary.Bottom) { markerIndex = this._terminal.markers.length; } else if (this._currentMarker === Boundary.Top) { markerIndex = 0; From 4ed6a47fd360585f9adfd4f0c21781ad9061b18e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 23 Oct 2019 22:10:44 +0200 Subject: [PATCH 43/89] Fixes #50163: Prefer end view position instead of next line's start view position when converting single-line model ranges --- .../common/viewModel/splitLinesCollection.ts | 25 +++++++++---- .../viewModel/viewModelDecorations.test.ts | 37 +++++++++++-------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 0704e236922..3cd2afbafaf 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -129,9 +129,7 @@ export class CoordinatesConverter implements ICoordinatesConverter { } public convertModelRangeToViewRange(modelRange: Range): Range { - let start = this._lines.convertModelPositionToViewPosition(modelRange.startLineNumber, modelRange.startColumn); - let end = this._lines.convertModelPositionToViewPosition(modelRange.endLineNumber, modelRange.endColumn); - return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + return this._lines.convertModelRangeToViewRange(modelRange); } public modelPositionIsVisible(modelPosition: Position): boolean { @@ -737,9 +735,9 @@ export class SplitLinesCollection implements IViewModelLinesCollection { public convertModelPositionToViewPosition(_modelLineNumber: number, _modelColumn: number): Position { this._ensureValidState(); - let validPosition = this.model.validatePosition(new Position(_modelLineNumber, _modelColumn)); - let inputLineNumber = validPosition.lineNumber; - let inputColumn = validPosition.column; + const validPosition = this.model.validatePosition(new Position(_modelLineNumber, _modelColumn)); + const inputLineNumber = validPosition.lineNumber; + const inputColumn = validPosition.column; let lineIndex = inputLineNumber - 1, lineIndexChanged = false; while (lineIndex > 0 && !this.lines[lineIndex].isVisible()) { @@ -751,7 +749,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { // console.log('in -> out ' + inputLineNumber + ',' + inputColumn + ' ===> ' + 1 + ',' + 1); return new Position(1, 1); } - let deltaLineNumber = 1 + (lineIndex === 0 ? 0 : this.prefixSumComputer.getAccumulatedValue(lineIndex - 1)); + const deltaLineNumber = 1 + (lineIndex === 0 ? 0 : this.prefixSumComputer.getAccumulatedValue(lineIndex - 1)); let r: Position; if (lineIndexChanged) { @@ -764,6 +762,19 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return r; } + public convertModelRangeToViewRange(modelRange: Range): Range { + let start = this.convertModelPositionToViewPosition(modelRange.startLineNumber, modelRange.startColumn); + let end = this.convertModelPositionToViewPosition(modelRange.endLineNumber, modelRange.endColumn); + if (modelRange.startLineNumber === modelRange.endLineNumber && start.lineNumber !== end.lineNumber) { + // This is a single line range that ends up taking more lines due to wrapping + if (end.column === this.getViewLineMinColumn(end.lineNumber)) { + // the end column lands on the first column of the next line + return new Range(start.lineNumber, start.column, end.lineNumber - 1, this.getViewLineMaxColumn(end.lineNumber - 1)); + } + } + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + private _getViewLineNumberForModelPosition(inputLineNumber: number, inputColumn: number): number { let lineIndex = inputLineNumber - 1; if (this.lines[lineIndex].isVisible()) { diff --git a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts index 9aab952c372..099e4d4e915 100644 --- a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts @@ -103,16 +103,6 @@ suite('ViewModelDecorations', () => { // view line 2: (1,14 -> 1,24) assert.deepEqual(inlineDecorations1, [ - { - range: new Range(1, 2, 2, 1), - inlineClassName: 'i-dec2', - type: InlineDecorationType.Regular - }, - { - range: new Range(2, 1, 2, 1), - inlineClassName: 'a-dec2', - type: InlineDecorationType.After - }, { range: new Range(1, 2, 2, 2), inlineClassName: 'i-dec3', @@ -124,7 +114,7 @@ suite('ViewModelDecorations', () => { type: InlineDecorationType.After }, { - range: new Range(1, 2, 4, 1), + range: new Range(1, 2, 3, 13), inlineClassName: 'i-dec4', type: InlineDecorationType.Regular }, @@ -164,7 +154,7 @@ suite('ViewModelDecorations', () => { type: InlineDecorationType.After }, { - range: new Range(2, 1, 4, 1), + range: new Range(2, 1, 3, 13), inlineClassName: 'i-dec8', type: InlineDecorationType.Regular }, @@ -199,7 +189,7 @@ suite('ViewModelDecorations', () => { type: InlineDecorationType.After }, { - range: new Range(2, 3, 4, 1), + range: new Range(2, 3, 3, 13), inlineClassName: 'i-dec11', type: InlineDecorationType.Regular }, @@ -228,30 +218,45 @@ suite('ViewModelDecorations', () => { // view line 3 (24 -> 36) assert.deepEqual(inlineDecorations2, [ { - range: new Range(1, 2, 4, 1), + range: new Range(1, 2, 3, 13), inlineClassName: 'i-dec4', type: InlineDecorationType.Regular }, + { + range: new Range(3, 13, 3, 13), + inlineClassName: 'a-dec4', + type: InlineDecorationType.After + }, { range: new Range(1, 2, 5, 8), inlineClassName: 'i-dec5', type: InlineDecorationType.Regular }, { - range: new Range(2, 1, 4, 1), + range: new Range(2, 1, 3, 13), inlineClassName: 'i-dec8', type: InlineDecorationType.Regular }, + { + range: new Range(3, 13, 3, 13), + inlineClassName: 'a-dec8', + type: InlineDecorationType.After + }, { range: new Range(2, 1, 5, 8), inlineClassName: 'i-dec9', type: InlineDecorationType.Regular }, { - range: new Range(2, 3, 4, 1), + range: new Range(2, 3, 3, 13), inlineClassName: 'i-dec11', type: InlineDecorationType.Regular }, + { + range: new Range(3, 13, 3, 13), + inlineClassName: 'a-dec11', + type: InlineDecorationType.After + }, { range: new Range(2, 3, 5, 8), inlineClassName: 'i-dec12', From 76a8e836c573d353e2e4dcbc688799115c9eba12 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 13:20:51 -0700 Subject: [PATCH 44/89] Use standard vscode.proposed.d.ts instead of copying it --- extensions/npm/src/typings/refs.d.ts | 1 + extensions/npm/src/vscode.proposed.d.ts | 1035 ----------------------- 2 files changed, 1 insertion(+), 1035 deletions(-) delete mode 100644 extensions/npm/src/vscode.proposed.d.ts diff --git a/extensions/npm/src/typings/refs.d.ts b/extensions/npm/src/typings/refs.d.ts index bc057c55878..954bab971e3 100644 --- a/extensions/npm/src/typings/refs.d.ts +++ b/extensions/npm/src/typings/refs.d.ts @@ -4,4 +4,5 @@ *--------------------------------------------------------------------------------------------*/ /// +/// /// diff --git a/extensions/npm/src/vscode.proposed.d.ts b/extensions/npm/src/vscode.proposed.d.ts deleted file mode 100644 index 535adbb5c02..00000000000 --- a/extensions/npm/src/vscode.proposed.d.ts +++ /dev/null @@ -1,1035 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * This is the place for API experiments and proposals. - * These API are NOT stable and subject to change. They are only available in the Insiders - * distribution and CANNOT be used in published extensions. - * - * To test these API in local environment: - * - Use Insiders release of VS Code. - * - Add `"enableProposedApi": true` to your package.json. - * - Copy this file to your project. - */ - -declare module 'vscode' { - - //#region Joh - call hierarchy - - export class CallHierarchyItem { - /** - * The name of this item. - */ - name: string; - - /** - * The kind of this item. - */ - kind: SymbolKind; - - /** - * Tags for this item. - */ - tags?: ReadonlyArray; - - /** - * More detail for this item, e.g. the signature of a function. - */ - detail?: string; - - /** - * The resource identifier of this item. - */ - uri: Uri; - - /** - * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. - */ - range: Range; - - /** - * The range that should be selected and reveal when this symbol is being picked, e.g. the name of a function. - * Must be contained by the [`range`](#CallHierarchyItem.range). - */ - selectionRange: Range; - - constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); - } - - export class CallHierarchyIncomingCall { - from: CallHierarchyItem; - fromRanges: Range[]; - constructor(item: CallHierarchyItem, fromRanges: Range[]); - } - - export class CallHierarchyOutgoingCall { - fromRanges: Range[]; - to: CallHierarchyItem; - constructor(item: CallHierarchyItem, fromRanges: Range[]); - } - - export interface CallHierarchyItemProvider { - - /** - * Provide a list of callers for the provided item, e.g. all function calling a function. - */ - provideCallHierarchyIncomingCalls(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - - /** - * Provide a list of calls for the provided item, e.g. all functions call from a function. - */ - provideCallHierarchyOutgoingCalls(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - - // todo@joh this could return as 'prepareCallHierarchy' (similar to the RenameProvider#prepareRename) - // - // /** - // * - // * Given a document and position compute a call hierarchy item. This is justed as - // * anchor for call hierarchy and then `resolveCallHierarchyItem` is being called. - // */ - // resolveCallHierarchyItem(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - export namespace languages { - export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyItemProvider): Disposable; - } - - //#endregion - - - //#region Alex - resolvers - - export interface RemoteAuthorityResolverContext { - resolveAttempt: number; - } - - export class ResolvedAuthority { - readonly host: string; - readonly port: number; - - constructor(host: string, port: number); - } - - export interface ResolvedOptions { - extensionHostEnv?: { [key: string]: string | null }; - } - - export type ResolverResult = ResolvedAuthority & ResolvedOptions; - - export class RemoteAuthorityResolverError extends Error { - static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; - static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; - - constructor(message?: string); - } - - export interface RemoteAuthorityResolver { - resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; - } - - export interface ResourceLabelFormatter { - scheme: string; - authority?: string; - formatting: ResourceLabelFormatting; - } - - export interface ResourceLabelFormatting { - label: string; // myLabel:/${path} - separator: '/' | '\\' | ''; - tildify?: boolean; - normalizeDriveLetter?: boolean; - workspaceSuffix?: string; - authorityPrefix?: string; - } - - export namespace workspace { - export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; - export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; - } - - //#endregion - - - // #region Joh - code insets - - export interface WebviewEditorInset { - readonly editor: TextEditor; - readonly line: number; - readonly height: number; - readonly webview: Webview; - readonly onDidDispose: Event; - dispose(): void; - } - - export namespace window { - export function createWebviewTextEditorInset(editor: TextEditor, line: number, height: number, options?: WebviewOptions): WebviewEditorInset; - } - - //#endregion - - //#region Joh - read/write in chunks - - export interface FileSystemProvider { - open?(resource: Uri, options: { create: boolean }): number | Thenable; - close?(fd: number): void | Thenable; - read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; - } - - //#endregion - - //#region Rob: search provider - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * A file glob pattern to match file paths against. - * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. - * @see [GlobPattern](#GlobPattern) - */ - export type GlobString = string; - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - - /** - * Options that apply to text search. - */ - export interface TextSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults: number; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Exclude files larger than `maxFileSize` in bytes. - */ - maxFileSize?: number; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - } - - /** - * The parameters of a query for file search. - */ - export interface FileSearchQuery { - /** - * The search pattern to match against file paths. - */ - pattern: string; - } - - /** - * Options that apply to file search. - */ - export interface FileSearchOptions extends SearchOptions { - /** - * The maximum number of results to be returned. - */ - maxResults?: number; - - /** - * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, - * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. - */ - session?: CancellationToken; - } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - export type TextSearchResult = TextSearchMatch | TextSearchContext; - - /** - * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. - * - * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for - * all files that match the user's query. - * - * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, - * and in that case, every file in the folder should be returned. - */ - export interface FileSearchProvider { - /** - * Provide the set of files that match a certain file path pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching files. - * @param token A cancellation token. - */ - provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; - } - - /** - * A TextSearchProvider provides search results for text results inside files in the workspace. - */ - export interface TextSearchProvider { - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; - } - - /** - * Options that can be set on a findTextInFiles search. - */ - export interface FindTextInFilesOptions { - /** - * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern - * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) - * to restrict the search results to a [workspace folder](#WorkspaceFolder). - */ - include?: GlobPattern; - - /** - * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern - * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will - * apply, when `null` no excludes will apply. - */ - exclude?: GlobPattern | null; - - /** - * The maximum number of results to search for - */ - maxResults?: number; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles?: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles?: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks?: boolean; - - /** - * Interpret files using this encoding. - * See the vscode setting `"files.encoding"` - */ - encoding?: string; - - /** - * Options to specify the size of the result text preview. - */ - previewOptions?: TextSearchPreviewOptions; - - /** - * Number of lines of context to include before each match. - */ - beforeContext?: number; - - /** - * Number of lines of context to include after each match. - */ - afterContext?: number; - } - - export namespace workspace { - /** - * Register a search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; - - /** - * Register a text search provider. - * - * Only one provider can be registered per scheme. - * - * @param scheme The provider will be invoked for workspace folders that have this file scheme. - * @param provider The provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - - /** - * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. - * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. - * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. - * @param callback A callback, called for each result - * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves when the search is complete. - */ - export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; - } - - //#endregion - - //#region Joao: diff command - - /** - * The contiguous set of modified lines in a diff. - */ - export interface LineChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - } - - export namespace commands { - - /** - * Registers a diff information command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. - * - * Diff information commands are different from ordinary [commands](#commands.registerCommand) as - * they only execute when there is an active diff editor when the command is called, and the diff - * information has been computed. Also, the command handler of an editor command has access to - * the diff information. - * - * @param command A unique identifier for the command. - * @param callback A command handler function with access to the [diff information](#LineChange). - * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. - */ - export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; - } - - //#endregion - - //#region Joh: decorations - - export class Decoration { - letter?: string; - title?: string; - color?: ThemeColor; - priority?: number; - bubble?: boolean; - } - - export interface DecorationProvider { - onDidChangeDecorations: Event; - provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerDecorationProvider(provider: DecorationProvider): Disposable; - } - - //#endregion - - //#region André: debug - - // deprecated - - export interface DebugConfigurationProvider { - /** - * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. - * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; - } - - /** - * Debug console mode used by debug session, see [options](#DebugSessionOptions). - */ - export enum DebugConsoleMode { - /** - * Debug session should have a separate debug console. - */ - Separate = 0, - - /** - * Debug session should share debug console with its parent session. - * This value has no effect for sessions which do not have a parent session. - */ - MergeWithParent = 1 - } - - /** - * Options for [starting a debug session](#debug.startDebugging). - */ - export interface DebugSessionOptions { - - /** - * When specified the newly created debug session is registered as a "child" session of this - * "parent" debug session. - */ - parentSession?: DebugSession; - - /** - * Controls whether this session should have a separate debug console or share it - * with the parent session. Has no effect for sessions which do not have a parent session. - * Defaults to Separate. - */ - consoleMode?: DebugConsoleMode; - } - - //#endregion - - //#region Rob, Matt: logging - - /** - * The severity level of a log message - */ - export enum LogLevel { - Trace = 1, - Debug = 2, - Info = 3, - Warning = 4, - Error = 5, - Critical = 6, - Off = 7 - } - - export namespace env { - /** - * Current logging level. - */ - export const logLevel: LogLevel; - - /** - * An [event](#Event) that fires when the log level has changed. - */ - export const onDidChangeLogLevel: Event; - } - - //#endregion - - //#region Joao: SCM validation - - /** - * Represents the validation type of the Source Control input. - */ - export enum SourceControlInputBoxValidationType { - - /** - * Something not allowed by the rules of a language or other means. - */ - Error = 0, - - /** - * Something suspicious but allowed. - */ - Warning = 1, - - /** - * Something to inform about but not a problem. - */ - Information = 2 - } - - export interface SourceControlInputBoxValidation { - - /** - * The validation message to display. - */ - readonly message: string; - - /** - * The validation type. - */ - readonly type: SourceControlInputBoxValidationType; - } - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * A validation function for the input box. It's possible to change - * the validation provider simply by setting this property to a different function. - */ - validateInput?(value: string, cursorPosition: number): ProviderResult; - } - - //#endregion - - //#region Joao: SCM selected provider - - export interface SourceControl { - - /** - * Whether the source control is selected. - */ - readonly selected: boolean; - - /** - * An event signaling when the selection state changes. - */ - readonly onDidChangeSelection: Event; - } - - //#endregion - - //#region Joao: SCM Input Box - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Controls whether the input box is visible (default is `true`). - */ - visible: boolean; - } - - //#endregion - - - //#region Terminal - - /** - * An [event](#Event) which fires when a [Terminal](#Terminal)'s dimensions change. - */ - export interface TerminalDimensionsChangeEvent { - /** - * The [terminal](#Terminal) for which the dimensions have changed. - */ - readonly terminal: Terminal; - /** - * The new value for the [terminal's dimensions](#Terminal.dimensions). - */ - readonly dimensions: TerminalDimensions; - } - - export interface TerminalDataWriteEvent { - /** - * The [terminal](#Terminal) for which the data was written. - */ - readonly terminal: Terminal; - /** - * The data being written. - */ - readonly data: string; - } - - namespace window { - /** - * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change. - */ - export const onDidChangeTerminalDimensions: Event; - - /** - * An event which fires when the terminal's pty slave pseudo-device is written to. In other - * words, this provides access to the raw data stream from the process running within the - * terminal, including VT sequences. - */ - export const onDidWriteTerminalData: Event; - } - - export interface Terminal { - /** - * The current dimensions of the terminal. This will be `undefined` immediately after the - * terminal is created as the dimensions are not known until shortly after the terminal is - * created. - */ - readonly dimensions: TerminalDimensions | undefined; - } - - //#endregion - - //#region Joh -> exclusive document filters - - export interface DocumentFilter { - exclusive?: boolean; - } - - //#endregion - - //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 - export interface FileRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - } - - export interface FileWillRenameEvent { - readonly oldUri: Uri; - readonly newUri: Uri; - waitUntil(thenable: Thenable): void; - } - - export namespace workspace { - export const onWillRenameFile: Event; - export const onDidRenameFile: Event; - } - //#endregion - - //#region Alex - OnEnter enhancement - export interface OnEnterRule { - /** - * This rule will only execute if the text above the this line matches this regular expression. - */ - oneLineAboveText?: RegExp; - } - //#endregion - - //#region Tree View - - export interface TreeView { - /** - * The tree view title is initially taken from the extension package.json - * Changes to the title property will be properly reflected in the UI in the title of the view. - */ - title?: string; - } - - /** - * Label describing the [Tree item](#TreeItem) - */ - export interface TreeItemLabel { - - /** - * A human-readable string describing the [Tree item](#TreeItem). - */ - label: string; - - /** - * Ranges in the label to highlight. A range is defined as a tuple of two number where the - * first is the inclusive start index and the second the exclusive end index - */ - highlights?: [number, number][]; - - } - - export class TreeItem2 extends TreeItem { - /** - * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). - */ - label?: string | TreeItemLabel | /* for compilation */ any; - - /** - * @param label Label describing this item - * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) - */ - constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); - } - //#endregion - - //#region CustomExecution - - - /** - * A task to execute - */ - export class Task2 extends Task { - detail?: string; - } - - export interface TaskPresentationOptions { - /** - * Controls whether the task is executed in a specific terminal group using split panes. - */ - group?: string; - } - //#endregion - - // #region Ben - status bar item with ID and Name - - export namespace window { - - /** - * Options to configure the status bar item. - */ - export interface StatusBarItemOptions { - - /** - * A unique identifier of the status bar item. The identifier - * is for example used to allow a user to show or hide the - * status bar item in the UI. - */ - id: string; - - /** - * A human readable name of the status bar item. The name is - * for example used as a label in the UI to show or hide the - * status bar item. - */ - name: string; - - /** - * The alignment of the status bar item. - */ - alignment?: StatusBarAlignment; - - /** - * The priority of the status bar item. Higher value means the item should - * be shown more to the left. - */ - priority?: number; - } - - /** - * Creates a status bar [item](#StatusBarItem). - * - * @param options The options of the item. If not provided, some default values - * will be assumed. For example, the `StatusBarItemOptions.id` will be the id - * of the extension and the `StatusBarItemOptions.name` will be the extension name. - * @return A new status bar item. - */ - export function createStatusBarItem(options?: StatusBarItemOptions): StatusBarItem; - } - - //#endregion - - // #region Ben - extension auth flow (desktop+web) - - export interface AppUriOptions { - payload?: { - path?: string; - query?: string; - fragment?: string; - }; - } - - export namespace env { - - /** - * Creates a Uri that - if opened in a browser - will result in a - * registered [UriHandler](#UriHandler) to fire. The handler's - * Uri will be configured with the path, query and fragment of - * [AppUriOptions](#AppUriOptions) if provided, otherwise it will be empty. - * - * Extensions should not make any assumptions about the resulting - * Uri and should not alter it in anyway. Rather, extensions can e.g. - * use this Uri in an authentication flow, by adding the Uri as - * callback query argument to the server to authenticate to. - * - * Note: If the server decides to add additional query parameters to the Uri - * (e.g. a token or secret), it will appear in the Uri that is passed - * to the [UriHandler](#UriHandler). - * - * **Example** of an authentication flow: - * ```typescript - * vscode.window.registerUriHandler({ - * handleUri(uri: vscode.Uri): vscode.ProviderResult { - * if (uri.path === '/did-authenticate') { - * console.log(uri.toString()); - * } - * } - * }); - * - * const callableUri = await vscode.env.createAppUri({ payload: { path: '/did-authenticate' } }); - * await vscode.env.openExternal(callableUri); - * ``` - */ - export function createAppUri(options?: AppUriOptions): Thenable; - } - - //#endregion - - //#region Custom editors, mjbvz - - export interface WebviewEditor extends WebviewPanel { - } - - export interface WebviewEditorProvider { - /** - * Fills out a `WebviewEditor` for a given resource. - * - * The provider should take ownership of passed in `editor`. - */ - resolveWebviewEditor( - resource: Uri, - editor: WebviewEditor - ): Thenable; - } - - namespace window { - export function registerWebviewEditorProvider( - viewType: string, - provider: WebviewEditorProvider, - options?: WebviewPanelOptions - ): Disposable; - } - - //#endregion -} From e0d2ed907d1b22808c56127678fb436d604586a7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 23 Oct 2019 22:41:20 +0200 Subject: [PATCH 45/89] Remove overviewRulerLanes from JSON schema --- src/vs/editor/common/config/editorOptions.ts | 5 ++--- src/vs/monaco.d.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 07a97988670..9743506a579 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -137,7 +137,7 @@ export interface IEditorOptions { fixedOverflowWidgets?: boolean; /** * The number of vertical lanes the overview ruler should render. - * Defaults to 2. + * Defaults to 3. */ overviewRulerLanes?: number; /** @@ -3094,8 +3094,7 @@ export const EditorOptions = { )), overviewRulerLanes: register(new EditorIntOption( EditorOption.overviewRulerLanes, 'overviewRulerLanes', - 3, 0, 3, - { description: nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler.") } + 3, 0, 3 )), parameterHints: register(new EditorParameterHints()), quickSuggestions: register(new EditorQuickSuggestions()), diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 46e4158c083..b2158819314 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2548,7 +2548,7 @@ declare namespace monaco.editor { fixedOverflowWidgets?: boolean; /** * The number of vertical lanes the overview ruler should render. - * Defaults to 2. + * Defaults to 3. */ overviewRulerLanes?: number; /** From 56cd791664aa93d583c14879bcafda25c39e0687 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:15:32 -0700 Subject: [PATCH 46/89] Extract has docs --- .../parameterHints/parameterHintsWidget.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index a6040059de6..6e94b83cfaa 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -236,19 +236,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, dom.append(this.domNodes.docs, renderedContents.element); } - let hasDocs = false; - if (activeParameter && typeof (activeParameter.documentation) === 'string' && activeParameter.documentation.length > 0) { - hasDocs = true; - } - if (activeParameter && typeof (activeParameter.documentation) === 'object' && activeParameter.documentation.value.length > 0) { - hasDocs = true; - } - if (typeof (signature.documentation) === 'string' && signature.documentation.length > 0) { - hasDocs = true; - } - if (typeof (signature.documentation) === 'object' && signature.documentation.value.length > 0) { - hasDocs = true; - } + const hasDocs = this.hasDocs(signature, activeParameter); dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs); dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); @@ -276,6 +264,22 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, this.domNodes.scrollbar.scanDomNode(); } + private hasDocs(signature: modes.SignatureInformation, activeParameter: modes.ParameterInformation | undefined): boolean { + if (activeParameter && typeof (activeParameter.documentation) === 'string' && activeParameter.documentation.length > 0) { + return true; + } + if (activeParameter && typeof (activeParameter.documentation) === 'object' && activeParameter.documentation.value.length > 0) { + return true; + } + if (typeof (signature.documentation) === 'string' && signature.documentation.length > 0) { + return true; + } + if (typeof (signature.documentation) === 'object' && signature.documentation.value.length > 0) { + return true; + } + return false; + } + private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, currentParameter: number): void { const [start, end] = this.getParameterLabelOffsets(signature, currentParameter); From 615d6408e58d9a296ea38013259a8ad951511940 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:16:14 -0700 Subject: [PATCH 47/89] Make sure we apply initial style to find widget Fixes #83176 --- .../contrib/webview/electron-browser/webviewElement.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index f722c074b5f..270746b76c8 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -287,6 +287,8 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme this._register(addDisposableListener(this.element!, 'found-in-page', e => { this._hasFindResult.fire(e.result.matches > 0); })); + + this.styledFindWidget(); } } @@ -341,10 +343,11 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme protected style(): void { super.style(); + this.styledFindWidget(); + } - if (this._webviewFindWidget) { - this._webviewFindWidget.updateTheme(this._webviewThemeDataProvider.getTheme()); - } + private styledFindWidget() { + this._webviewFindWidget?.updateTheme(this._webviewThemeDataProvider.getTheme()); } private readonly _hasFindResult = this._register(new Emitter()); From 1cac8b18d54017e9cbad43eb0e27541da12bb3ca Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Oct 2019 00:18:30 +0200 Subject: [PATCH 48/89] Fixes #50304: Tweaks to visible ranges queries for lines > 10k chars --- .../editor/browser/controller/mouseHandler.ts | 5 +- .../editor/browser/controller/mouseTarget.ts | 12 ++--- .../browser/controller/textAreaHandler.ts | 6 +-- src/vs/editor/browser/view/viewImpl.ts | 2 +- .../viewParts/decorations/decorations.ts | 3 ++ .../browser/viewParts/lines/viewLine.ts | 24 +++++---- .../browser/viewParts/lines/viewLines.ts | 52 +++++-------------- .../viewParts/viewCursors/viewCursor.ts | 13 +++-- src/vs/editor/common/view/renderingContext.ts | 38 +++++++++----- 9 files changed, 76 insertions(+), 79 deletions(-) diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 357f1f542be..d1a19841028 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -17,13 +17,12 @@ import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/v import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; - /** * Merges mouse events when mouse move events are throttled */ @@ -59,7 +58,7 @@ export interface IPointerHandlerHelper { */ getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position | null; - visibleRangeForPosition2(lineNumber: number, column: number): HorizontalRange | null; + visibleRangeForPosition(lineNumber: number, column: number): HorizontalPosition | null; getLineWidth(lineNumber: number): number; } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index c396ab9b09d..1f737774248 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -13,7 +13,7 @@ import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/v import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; -import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; @@ -346,8 +346,8 @@ export class HitTestContext { return this._viewHelper.getLineWidth(lineNumber); } - public visibleRangeForPosition2(lineNumber: number, column: number): HorizontalRange | null { - return this._viewHelper.visibleRangeForPosition2(lineNumber, column); + public visibleRangeForPosition(lineNumber: number, column: number): HorizontalPosition | null { + return this._viewHelper.visibleRangeForPosition(lineNumber, column); } public getPositionFromDOMInfo(spanNode: HTMLElement, offset: number): Position | null { @@ -743,7 +743,7 @@ export class MouseTargetFactory { return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, undefined, detail); } - const visibleRange = ctx.visibleRangeForPosition2(lineNumber, column); + const visibleRange = ctx.visibleRangeForPosition(lineNumber, column); if (!visibleRange) { return request.fulfill(MouseTargetType.UNKNOWN, pos); @@ -761,14 +761,14 @@ export class MouseTargetFactory { const points: OffsetColumn[] = []; points.push({ offset: visibleRange.left, column: column }); if (column > 1) { - const visibleRange = ctx.visibleRangeForPosition2(lineNumber, column - 1); + const visibleRange = ctx.visibleRangeForPosition(lineNumber, column - 1); if (visibleRange) { points.push({ offset: visibleRange.left, column: column - 1 }); } } const lineMaxColumn = ctx.model.getLineMaxColumn(lineNumber); if (column < lineMaxColumn) { - const visibleRange = ctx.visibleRangeForPosition2(lineNumber, column + 1); + const visibleRange = ctx.visibleRangeForPosition(lineNumber, column + 1); if (visibleRange) { points.push({ offset: visibleRange.left, column: column + 1 }); } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 78032cfd17c..df5c43b21be 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -25,13 +25,13 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference } from 'vs/editor/common/model'; -import { HorizontalRange, RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { RenderingContext, RestrictedRenderingContext, HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; export interface ITextAreaHandlerHelper { - visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalRange | null; + visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalPosition | null; } class VisibleTextAreaData { @@ -408,7 +408,7 @@ export class TextAreaHandler extends ViewPart { // --- end view API - private _primaryCursorVisibleRange: HorizontalRange | null = null; + private _primaryCursorVisibleRange: HorizontalPosition | null = null; public prepareRender(ctx: RenderingContext): void { const primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 9f2e96a1a38..97c740c526c 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -261,7 +261,7 @@ export class View extends ViewEventHandler { return this.viewLines.getPositionFromDOMInfo(spanNode, offset); }, - visibleRangeForPosition2: (lineNumber: number, column: number) => { + visibleRangeForPosition: (lineNumber: number, column: number) => { this._flushAccumulatedAndRenderNow(); return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column)); }, diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 7ba28975519..e8b5e779862 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -195,6 +195,9 @@ export class DecorationsOverlay extends DynamicViewOverlay { for (let j = 0, lenJ = linesVisibleRanges.length; j < lenJ; j++) { const lineVisibleRanges = linesVisibleRanges[j]; + if (lineVisibleRanges.outsideRenderedLine) { + continue; + } const lineIndex = lineVisibleRanges.lineNumber - visibleStartLineNumber; if (showIfCollapsed && lineVisibleRanges.ranges.length === 1) { diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index b3b0ac98942..655b93462d3 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -10,7 +10,7 @@ import { IVisibleLine } from 'vs/editor/browser/view/viewLayer'; import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil'; import { IStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { IConfiguration } from 'vs/editor/common/editorCommon'; -import { HorizontalRange } from 'vs/editor/common/view/renderingContext'; +import { HorizontalRange, VisibleRanges } from 'vs/editor/common/view/renderingContext'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, LineRange } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; @@ -295,7 +295,7 @@ export class ViewLine implements IVisibleLine { return this._renderedViewLine.getWidthIsFast(); } - public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): VisibleRanges | null { if (!this._renderedViewLine) { return null; } @@ -306,21 +306,27 @@ export class ViewLine implements IVisibleLine { endColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, endColumn)); const stopRenderingLineAfter = this._renderedViewLine.input.stopRenderingLineAfter | 0; // @perf + let outsideRenderedLine = false; - if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter && endColumn > stopRenderingLineAfter) { + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1 && endColumn > stopRenderingLineAfter + 1) { // This range is obviously not visible - return null; + outsideRenderedLine = true; } - if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter) { - startColumn = stopRenderingLineAfter; + if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1) { + startColumn = stopRenderingLineAfter + 1; } - if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter) { - endColumn = stopRenderingLineAfter; + if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter + 1) { + endColumn = stopRenderingLineAfter + 1; } - return this._renderedViewLine.getVisibleRangesForRange(startColumn, endColumn, context); + const horizontalRanges = this._renderedViewLine.getVisibleRangesForRange(startColumn, endColumn, context); + if (horizontalRanges && horizontalRanges.length > 0) { + return new VisibleRanges(outsideRenderedLine, horizontalRanges); + } + + return null; } public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index ff3a504d5cf..8a455e93bd1 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -14,7 +14,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { HorizontalRange, IViewLines, LineVisibleRanges } from 'vs/editor/common/view/renderingContext'; +import { IViewLines, LineVisibleRanges, VisibleRanges, HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; @@ -390,7 +390,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, const endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); const visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); - if (!visibleRangesForLine || visibleRangesForLine.length === 0) { + if (!visibleRangesForLine) { continue; } @@ -399,11 +399,11 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, nextLineModelLineNumber = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber + 1, 1)).lineNumber; if (currentLineModelLineNumber !== nextLineModelLineNumber) { - visibleRangesForLine[visibleRangesForLine.length - 1].width += this._typicalHalfwidthCharacterWidth; + visibleRangesForLine.ranges[visibleRangesForLine.ranges.length - 1].width += this._typicalHalfwidthCharacterWidth; } } - visibleRanges[visibleRangesLen++] = new LineVisibleRanges(lineNumber, visibleRangesForLine); + visibleRanges[visibleRangesLen++] = new LineVisibleRanges(visibleRangesForLine.outsideRenderedLine, lineNumber, visibleRangesForLine.ranges); } if (visibleRangesLen === 0) { @@ -413,54 +413,26 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return visibleRanges; } - private visibleRangesForRange2(_range: Range): HorizontalRange[] | null { - + private _visibleRangesForLineRange(lineNumber: number, startColumn: number, endColumn: number): VisibleRanges | null { if (this.shouldRender()) { // Cannot read from the DOM because it is dirty // i.e. the model & the dom are out of sync, so I'd be reading something stale return null; } - const range = Range.intersectRanges(_range, this._lastRenderedData.getCurrentVisibleRange()); - if (!range) { + if (lineNumber < this._visibleLines.getStartLineNumber() || lineNumber > this._visibleLines.getEndLineNumber()) { return null; } - let result: HorizontalRange[] = []; - const domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); - - const rendStartLineNumber = this._visibleLines.getStartLineNumber(); - const rendEndLineNumber = this._visibleLines.getEndLineNumber(); - for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { - - if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { - continue; - } - - const startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; - const endColumn = lineNumber === range.endLineNumber ? range.endColumn : this._context.model.getLineMaxColumn(lineNumber); - const visibleRangesForLine = this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, domReadingContext); - - if (!visibleRangesForLine || visibleRangesForLine.length === 0) { - continue; - } - - result = result.concat(visibleRangesForLine); - } - - if (result.length === 0) { - return null; - } - - return result; + return this._visibleLines.getVisibleLine(lineNumber).getVisibleRangesForRange(startColumn, endColumn, new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot)); } - public visibleRangeForPosition(position: Position): HorizontalRange | null { - const visibleRanges = this.visibleRangesForRange2(new Range(position.lineNumber, position.column, position.lineNumber, position.column)); + public visibleRangeForPosition(position: Position): HorizontalPosition | null { + const visibleRanges = this._visibleRangesForLineRange(position.lineNumber, position.column, position.column); if (!visibleRanges) { return null; } - return visibleRanges[0]; + return new HorizontalPosition(visibleRanges.outsideRenderedLine, visibleRanges.ranges[0].left); } // --- implementation @@ -641,7 +613,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, const viewportStartX = viewport.left; const viewportEndX = viewportStartX + viewport.width; - const visibleRanges = this.visibleRangesForRange2(new Range(lineNumber, startColumn, lineNumber, endColumn)); + const visibleRanges = this._visibleRangesForLineRange(lineNumber, startColumn, endColumn); let boxStartX = Constants.MAX_SAFE_SMALL_INTEGER; let boxEndX = 0; @@ -653,7 +625,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, }; } - for (const visibleRange of visibleRanges) { + for (const visibleRange of visibleRanges.ranges) { if (visibleRange.left < boxStartX) { boxStartX = visibleRange.left; } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index cc5e722fca7..6173ba7a6fa 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -121,7 +121,7 @@ export class ViewCursor { if (this._cursorStyle === TextEditorCursorStyle.Line || this._cursorStyle === TextEditorCursorStyle.LineThin) { const visibleRange = ctx.visibleRangeForPosition(this._position); - if (!visibleRange) { + if (!visibleRange || visibleRange.outsideRenderedLine) { // Outside viewport return null; } @@ -151,13 +151,18 @@ export class ViewCursor { const lineContent = this._context.model.getLineContent(this._position.lineNumber); const nextCharLength = strings.nextCharLength(lineContent, this._position.column - 1); const visibleRangeForCharacter = ctx.linesVisibleRangesForRange(new Range(this._position.lineNumber, this._position.column, this._position.lineNumber, this._position.column + nextCharLength), false); - - if (!visibleRangeForCharacter || visibleRangeForCharacter.length === 0 || visibleRangeForCharacter[0].ranges.length === 0) { + if (!visibleRangeForCharacter || visibleRangeForCharacter.length === 0) { // Outside viewport return null; } - const range = visibleRangeForCharacter[0].ranges[0]; + const firstVisibleRangeForCharacter = visibleRangeForCharacter[0]; + if (firstVisibleRangeForCharacter.outsideRenderedLine || firstVisibleRangeForCharacter.ranges.length === 0) { + // Outside viewport + return null; + } + + const range = firstVisibleRangeForCharacter.ranges[0]; const width = range.width < 1 ? this._typicalHalfwidthCharacterWidth : range.width; let textContentClassName = ''; diff --git a/src/vs/editor/common/view/renderingContext.ts b/src/vs/editor/common/view/renderingContext.ts index 6a3e7beba91..c7b9665b7a4 100644 --- a/src/vs/editor/common/view/renderingContext.ts +++ b/src/vs/editor/common/view/renderingContext.ts @@ -10,7 +10,7 @@ import { IViewLayout, ViewModelDecoration } from 'vs/editor/common/viewModel/vie export interface IViewLines { linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] | null; - visibleRangeForPosition(position: Position): HorizontalRange | null; + visibleRangeForPosition(position: Position): HorizontalPosition | null; } export abstract class RestrictedRenderingContext { @@ -77,26 +77,20 @@ export class RenderingContext extends RestrictedRenderingContext { return this._viewLines.linesVisibleRangesForRange(range, includeNewLines); } - public visibleRangeForPosition(position: Position): HorizontalRange | null { + public visibleRangeForPosition(position: Position): HorizontalPosition | null { return this._viewLines.visibleRangeForPosition(position); } } export class LineVisibleRanges { - _lineVisibleRangesBrand: void; - - public lineNumber: number; - public ranges: HorizontalRange[]; - - constructor(lineNumber: number, ranges: HorizontalRange[]) { - this.lineNumber = lineNumber; - this.ranges = ranges; - } + constructor( + public readonly outsideRenderedLine: boolean, + public readonly lineNumber: number, + public readonly ranges: HorizontalRange[] + ) { } } export class HorizontalRange { - _horizontalRangeBrand: void; - public left: number; public width: number; @@ -109,3 +103,21 @@ export class HorizontalRange { return `[${this.left},${this.width}]`; } } + +export class HorizontalPosition { + public outsideRenderedLine: boolean; + public left: number; + + constructor(outsideRenderedLine: boolean, left: number) { + this.outsideRenderedLine = outsideRenderedLine; + this.left = Math.round(left); + } +} + +export class VisibleRanges { + constructor( + public readonly outsideRenderedLine: boolean, + public readonly ranges: HorizontalRange[] + ) { + } +} From 00cd1a88e95f31f37416e5dcb74b659fd223e7bc Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 23 Oct 2019 16:57:19 -0700 Subject: [PATCH 49/89] Fix #80707. --- .../pieceTreeTextBuffer/pieceTreeBase.ts | 21 ++++++++++++++----- src/vs/editor/common/model/textModelSearch.ts | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 9e38117e9e7..1c844dd66c4 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -577,23 +577,34 @@ export class PieceTreeBase { let m: RegExpExecArray | null; // Reset regex to search from the beginning - searcher.reset(start); let ret: BufferCursor = { line: 0, column: 0 }; + let searchText: string; + let offsetInBuffer: (offset: number) => number; + + if (searcher._wordSeparators) { + searchText = buffer.buffer.substring(start, end); + offsetInBuffer = (offset: number) => offset + start; + searcher.reset(-1); + } else { + searchText = buffer.buffer; + offsetInBuffer = (offset: number) => offset; + searcher.reset(start); + } do { - m = searcher.next(buffer.buffer); + m = searcher.next(searchText); if (m) { - if (m.index >= end) { + if (offsetInBuffer(m.index) >= end) { return resultLen; } - this.positionInBuffer(node, m.index - startOffsetInBuffer, ret); + this.positionInBuffer(node, offsetInBuffer(m.index) - startOffsetInBuffer, ret); let lineFeedCnt = this.getLineFeedCnt(node.piece.bufferIndex, startCursor, ret); let retStartColumn = ret.line === startCursor.line ? ret.column - startCursor.column + startColumn : ret.column + 1; let retEndColumn = retStartColumn + m[0].length; result[resultLen++] = createFindMatch(new Range(startLineNumber + lineFeedCnt, retStartColumn, startLineNumber + lineFeedCnt, retEndColumn), m, captureMatches); - if (m.index + m[0].length >= end) { + if (offsetInBuffer(m.index) + m[0].length >= end) { return resultLen; } if (resultLen >= limitResultCount) { diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index 4622af82cbc..d41d1a2a1ba 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -510,7 +510,7 @@ export function isValidMatch(wordSeparators: WordCharacterClassifier, text: stri } export class Searcher { - private readonly _wordSeparators: WordCharacterClassifier | null; + public readonly _wordSeparators: WordCharacterClassifier | null; private readonly _searchRegex: RegExp; private _prevMatchStartIndex: number; private _prevMatchLength: number; From f68737bdcce2fedf0ca4d79358a566d4c78a5535 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 17:41:35 -0700 Subject: [PATCH 50/89] Use optional chaining in terminalInstance --- .../terminal/browser/terminalInstance.ts | 74 +++++-------------- 1 file changed, 19 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 46bd862a935..6ae79f42707 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -524,9 +524,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { throw new Error('The terminal instance has not been attached to a container yet'); } - if (this._wrapperElement.parentNode) { - this._wrapperElement.parentNode.removeChild(this._wrapperElement); - } + this._wrapperElement.parentNode?.removeChild(this._wrapperElement); this._container = container; this._container.appendChild(this._wrapperElement); } @@ -544,9 +542,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } // The container changed, reattach - if (this._container) { - this._container.removeChild(this._wrapperElement); - } + this._container?.removeChild(this._wrapperElement); this._container = container; this._container.appendChild(this._wrapperElement); } @@ -652,11 +648,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const widgetManager = new TerminalWidgetManager(this._wrapperElement); this._widgetManager = widgetManager; - this._processManager.onProcessReady(() => { - if (this._linkHandler) { - this._linkHandler.setWidgetManager(widgetManager); - } - }); + this._processManager.onProcessReady(() => this._linkHandler?.setWidgetManager(widgetManager)); const computedStyle = window.getComputedStyle(this._container); const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); @@ -751,19 +743,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } public clearSelection(): void { - if (!this._xterm) { - return; - } - this._xterm.clearSelection(); + this._xterm?.clearSelection(); } public selectAll(): void { - if (!this._xterm) { - return; - } // Focus here to ensure the terminal context key is set - this._xterm.focus(); - this._xterm.selectAll(); + this._xterm?.focus(); + this._xterm?.selectAll(); } public findNext(term: string, searchOptions: ISearchOptions): boolean { @@ -924,45 +910,31 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } public scrollDownLine(): void { - if (this._xterm) { - this._xterm.scrollLines(1); - } + this._xterm?.scrollLines(1); } public scrollDownPage(): void { - if (this._xterm) { - this._xterm.scrollPages(1); - } + this._xterm?.scrollPages(1); } public scrollToBottom(): void { - if (this._xterm) { - this._xterm.scrollToBottom(); - } + this._xterm?.scrollToBottom(); } public scrollUpLine(): void { - if (this._xterm) { - this._xterm.scrollLines(-1); - } + this._xterm?.scrollLines(-1); } public scrollUpPage(): void { - if (this._xterm) { - this._xterm.scrollPages(-1); - } + this._xterm?.scrollPages(-1); } public scrollToTop(): void { - if (this._xterm) { - this._xterm.scrollToTop(); - } + this._xterm?.scrollToTop(); } public clear(): void { - if (this._xterm) { - this._xterm.clear(); - } + this._xterm?.clear(); } private _refreshSelectionContextKey() { @@ -1019,12 +991,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } private _onProcessData(data: string): void { - if (this._widgetManager) { - this._widgetManager.closeMessage(); - } - if (this._xterm) { - this._xterm.write(data); - } + this._widgetManager?.closeMessage(); + this._xterm?.write(data); } /** @@ -1130,10 +1098,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public reuseTerminal(shell: IShellLaunchConfig): void { // Unsubscribe any key listener we may have. - if (this._pressAnyKeyToCloseListener) { - this._pressAnyKeyToCloseListener.dispose(); - this._pressAnyKeyToCloseListener = undefined; - } + this._pressAnyKeyToCloseListener?.dispose(); + this._pressAnyKeyToCloseListener = undefined; // Kill and clear up the process, making the process manager ready for a new process this._processManager.dispose(); @@ -1256,10 +1222,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey); this._xterm!.loadAddon(this._navigationModeAddon); } else { - if (this._navigationModeAddon) { - this._navigationModeAddon.dispose(); - this._navigationModeAddon = undefined; - } + this._navigationModeAddon?.dispose(); + this._navigationModeAddon = undefined; } this._xterm!.setOption('screenReaderMode', isEnabled); } From ff2a1b874669a37634cb9283c0962f208228a035 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:21:49 -0700 Subject: [PATCH 51/89] model is never mutated --- .../parameterHints/parameterHintsWidget.ts | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 6e94b83cfaa..ec8cf3b2879 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -8,7 +8,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Event } from 'vs/base/common/event'; -import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./parameterHints'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -31,7 +31,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, private readonly markdownRenderer: MarkdownRenderer; private readonly renderDisposeables = this._register(new DisposableStore()); - private readonly model = this._register(new MutableDisposable()); + private readonly model: ParameterHintsModel; private readonly keyVisible: IContextKey; private readonly keyMultipleSignatures: IContextKey; @@ -57,11 +57,11 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, ) { super(); this.markdownRenderer = this._register(new MarkdownRenderer(editor, modeService, openerService)); - this.model.value = new ParameterHintsModel(editor); + this.model = this._register(new ParameterHintsModel(editor)); this.keyVisible = Context.Visible.bindTo(contextKeyService); this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); - this._register(this.model.value.onChangedHints(newParameterHints => { + this._register(this.model.onChangedHints(newParameterHints => { if (newParameterHints) { this.show(); this.render(newParameterHints); @@ -134,7 +134,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private show(): void { - if (!this.model || this.visible) { + if (this.visible) { return; } @@ -153,7 +153,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private hide(): void { - if (!this.model || !this.visible) { + if (!this.visible) { return; } @@ -189,7 +189,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, this.domNodes.docs.innerHTML = ''; const signature = hints.signatures[hints.activeSignature]; - if (!signature) { return; } @@ -204,7 +203,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, if (!hasParameters) { const label = dom.append(code, $('span')); label.textContent = signature.label; - } else { this.renderParameters(code, signature, hints.activeParameter); } @@ -242,7 +240,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); let currentOverload = String(hints.activeSignature + 1); - if (hints.signatures.length < 10) { currentOverload += `/${hints.signatures.length}`; } @@ -281,7 +278,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } private renderParameters(parent: HTMLElement, signature: modes.SignatureInformation, currentParameter: number): void { - const [start, end] = this.getParameterLabelOffsets(signature, currentParameter); const beforeSpan = document.createElement('span'); @@ -321,23 +317,17 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } next(): void { - if (this.model.value) { - this.editor.focus(); - this.model.value.next(); - } + this.editor.focus(); + this.model.next(); } previous(): void { - if (this.model.value) { - this.editor.focus(); - this.model.value.previous(); - } + this.editor.focus(); + this.model.previous(); } cancel(): void { - if (this.model.value) { - this.model.value.cancel(); - } + this.model.cancel(); } getDomNode(): HTMLElement { @@ -352,9 +342,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, } trigger(context: TriggerContext): void { - if (this.model.value) { - this.model.value.trigger(context, 0); - } + this.model.trigger(context, 0); } private updateMaxHeight(): void { From b87445c0f59170f907992ec16559486276bf52f0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:23:40 -0700 Subject: [PATCH 52/89] Remove extre implements --- src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index ec8cf3b2879..80983c2bac7 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -8,7 +8,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Event } from 'vs/base/common/event'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./parameterHints'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -25,7 +25,7 @@ import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameter const $ = dom.$; -export class ParameterHintsWidget extends Disposable implements IContentWidget, IDisposable { +export class ParameterHintsWidget extends Disposable implements IContentWidget { private static readonly ID = 'editor.widget.parameterHintsWidget'; From 000cff9dd53c8b998dc13f41d7497c0762301cde Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:24:44 -0700 Subject: [PATCH 53/89] Explicitly note that activeParameter could be undefined --- src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 80983c2bac7..970799fb896 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -209,7 +209,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.renderDisposeables.clear(); - const activeParameter = signature.parameters[hints.activeParameter]; + const activeParameter: modes.ParameterInformation | undefined = signature.parameters[hints.activeParameter]; if (activeParameter && activeParameter.documentation) { const documentation = $('span.documentation'); From 611fd60cd4f624b5a6a74c9a2b5a0b285c6e07a0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:43:28 -0700 Subject: [PATCH 54/89] Make sure we update js/ts diagnostics when the visible editors change Changes in one document may cause errors in other, not yet visible docs. Make sure we update the diagnostics when another file becomes visible to handle that case --- .../src/features/bufferSyncSupport.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 8d51a63215b..3675518a0f4 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -349,6 +349,14 @@ export default class BufferSyncSupport extends Disposable { vscode.workspace.onDidOpenTextDocument(this.openTextDocument, this, this._disposables); vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this._disposables); vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this._disposables); + vscode.window.onDidChangeVisibleTextEditors(e => { + for (const { document } of e) { + const syncedBuffer = this.syncedBuffers.get(document.uri); + if (syncedBuffer) { + this.requestDiagnostic(syncedBuffer); + } + } + }, this, this._disposables); vscode.workspace.textDocuments.forEach(this.openTextDocument, this); } From afe635972050117543dca1719a78109863f86e6e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:54:30 -0700 Subject: [PATCH 55/89] Use ?. --- .../webview/browser/webviewCommands.ts | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 62a3eecece5..7231399419d 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -13,44 +13,32 @@ import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEdito export class ShowWebViewEditorFindWidgetCommand extends Command { public static readonly ID = 'editor.action.webvieweditor.showFind'; - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.showFind(); - } + public runCommand(accessor: ServicesAccessor): void { + getActiveWebviewEditor(accessor)?.showFind(); } } export class HideWebViewEditorFindCommand extends Command { public static readonly ID = 'editor.action.webvieweditor.hideFind'; - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.hideFind(); - } + public runCommand(accessor: ServicesAccessor): void { + getActiveWebviewEditor(accessor)?.hideFind(); } } export class WebViewEditorFindNextCommand extends Command { public static readonly ID = 'editor.action.webvieweditor.findNext'; - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.find(false); - } + public runCommand(accessor: ServicesAccessor): void { + getActiveWebviewEditor(accessor)?.find(false); } } export class WebViewEditorFindPreviousCommand extends Command { public static readonly ID = 'editor.action.webvieweditor.findPrevious'; - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.find(true); - } + public runCommand(accessor: ServicesAccessor): void { + getActiveWebviewEditor(accessor)?.find(true); } } export class ReloadWebviewAction extends Action { @@ -79,8 +67,8 @@ export class ReloadWebviewAction extends Action { } } -function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | null { +function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | undefined { const editorService = accessor.get(IEditorService); - const activeControl = editorService.activeControl as WebviewEditor; - return activeControl.isWebviewEditor ? activeControl : null; + const activeControl = editorService.activeControl as WebviewEditor | undefined; + return activeControl?.isWebviewEditor ? activeControl : undefined; } From 32db47ae2bbfbf042f57d62508f9f96989523ca4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 14:56:07 -0700 Subject: [PATCH 56/89] Remove duplicate impl of getActiveWebviewEditor --- .../workbench/contrib/webview/browser/webviewCommands.ts | 2 +- .../contrib/webview/electron-browser/webviewCommands.ts | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 7231399419d..bfde6b8520e 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -67,7 +67,7 @@ export class ReloadWebviewAction extends Action { } } -function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | undefined { +export function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | undefined { const editorService = accessor.get(IEditorService); const activeControl = editorService.activeControl as WebviewEditor | undefined; return activeControl?.isWebviewEditor ? activeControl : undefined; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index e9e709e18e0..40cc80f75f6 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -6,11 +6,10 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { Command, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewTag } from 'electron'; +import { getActiveWebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewCommands'; export class OpenWebviewDeveloperToolsAction extends Action { static readonly ID = 'workbench.action.webview.openDeveloperTools'; @@ -82,12 +81,6 @@ export class RedoWebviewEditorCommand extends Command { } } -function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | undefined { - const editorService = accessor.get(IEditorService); - const activeControl = editorService.activeControl as WebviewEditor; - return activeControl.isWebviewEditor ? activeControl : undefined; -} - function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview: ElectronWebviewBasedWebview) => void): void { const webViewEditor = getActiveWebviewEditor(accessor); if (webViewEditor) { From 45cd966a16e78b7e9bee0d64afd7d6b2f2394c9e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 16:09:18 -0700 Subject: [PATCH 57/89] Use VS Code's fs instead of node --- .../src/features/updatePathsOnRename.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 9333e6f75c0..68d641f316c 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; @@ -22,15 +21,12 @@ const localize = nls.loadMessageBundle(); const updateImportsOnFileMoveName = 'updateImportsOnFileMove.enabled'; -function isDirectory(path: string): Promise { - return new Promise((resolve, reject) => { - fs.stat(path, (err, stat) => { - if (err) { - return reject(err); - } - return resolve(stat.isDirectory()); - }); - }); +async function isDirectory(resource: vscode.Uri): Promise { + try { + return (await vscode.workspace.fs.stat(resource)).type === vscode.FileType.Directory; + } catch { + return false; + } } const enum UpdateImportsOnFileMoveSetting { @@ -198,7 +194,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return undefined; } - if (await isDirectory(resource.fsPath)) { + if (await isDirectory(resource)) { const files = await vscode.workspace.findFiles({ base: resource.fsPath, pattern: '**/*.{ts,tsx,js,jsx}', From 0d0a5741640248ca95097d9eed8808957a53386a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 16:10:57 -0700 Subject: [PATCH 58/89] Normalizing names --- .../src/features/updatePathsOnRename.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 68d641f316c..fc4e10315ee 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -66,13 +66,13 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return; } - const newFile = this.client.toPath(newResource); - if (!newFile) { + const newFilePath = this.client.toPath(newResource); + if (!newFilePath) { return; } - const oldFile = this.client.toPath(oldResource); - if (!oldFile) { + const oldFilePath = this.client.toPath(oldResource); + if (!oldFilePath) { return; } @@ -88,7 +88,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { this.client.bufferSyncSupport.closeResource(oldResource); this.client.bufferSyncSupport.openTextDocument(document); - const edits = await this.getEditsForFileRename(document, oldFile, newFile); + const edits = await this.getEditsForFileRename(document, oldFilePath, newFilePath); if (!edits || !edits.size) { return; } @@ -207,18 +207,18 @@ class UpdateImportsOnFileRenameHandler extends Disposable { private async getEditsForFileRename( document: vscode.TextDocument, - oldFile: string, - newFile: string, + oldFilePath: string, + newFilePath: string, ): Promise { const response = await this.client.interruptGetErr(() => { this.fileConfigurationManager.setGlobalConfigurationFromDocument(document, nulToken); const args: Proto.GetEditsForFileRenameRequestArgs = { - oldFilePath: oldFile, - newFilePath: newFile, + oldFilePath, + newFilePath, }; return this.client.execute('getEditsForFileRename', args, nulToken); }); - if (response.type !== 'response' || !response.body) { + if (response.type !== 'response') { return; } From 46d2224d8b35265c155ac8607dbf9811f4cfec38 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 16:19:29 -0700 Subject: [PATCH 59/89] Remove gating for TS versions < 2.4 Telemetry shows that these old TS versions have very low usage (in the tens across the many, many js/ts users). Given this, it now makes sense to remove the extra gating/checks required to support these old versions --- .../src/features/bufferSyncSupport.ts | 5 +- .../features/directiveCommentCompletions.ts | 11 ++--- .../src/features/implementations.ts | 12 ++--- .../src/features/implementationsCodeLens.ts | 13 ++--- .../src/tsServer/spawner.ts | 48 ++++++++----------- .../src/typeScriptServiceClientHost.ts | 5 -- .../src/typescriptServiceClient.ts | 8 ---- .../src/utils/api.ts | 4 -- 8 files changed, 32 insertions(+), 74 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 3675518a0f4..3aaf6e2c564 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -176,6 +176,7 @@ class SyncedBuffer { const args: Proto.OpenRequestArgs = { file: this.filepath, fileContent: this.document.getText(), + projectRootPath: this.client.getWorkspaceRootForResource(this.document.uri), }; const scriptKind = mode2ScriptKind(this.document.languageId); @@ -183,10 +184,6 @@ class SyncedBuffer { args.scriptKindName = scriptKind; } - if (this.client.apiVersion.gte(API.v230)) { - args.projectRootPath = this.client.getWorkspaceRootForResource(this.document.uri); - } - if (this.client.apiVersion.gte(API.v240)) { const tsPluginsForDocument = this.client.pluginManager.plugins .filter(x => x.languages.indexOf(this.document.languageId) >= 0); diff --git a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts index 0b1ddeca929..c0a222e47df 100644 --- a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts +++ b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts @@ -6,8 +6,6 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; const localize = nls.loadMessageBundle(); @@ -36,7 +34,6 @@ const directives: Directive[] = [ ]; class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvider { - public static readonly minVersion = API.v230; constructor( private readonly client: ITypeScriptServiceClient, @@ -71,9 +68,7 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, DirectiveCommentCompletionProvider.minVersion, () => { - return vscode.languages.registerCompletionItemProvider(selector, - new DirectiveCommentCompletionProvider(client), - '@'); - }); + return vscode.languages.registerCompletionItemProvider(selector, + new DirectiveCommentCompletionProvider(client), + '@'); } diff --git a/extensions/typescript-language-features/src/features/implementations.ts b/extensions/typescript-language-features/src/features/implementations.ts index 7b7dc130175..c7cdeeb755f 100644 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ b/extensions/typescript-language-features/src/features/implementations.ts @@ -5,13 +5,9 @@ import * as vscode from 'vscode'; import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; import DefinitionProviderBase from './definitionProviderBase'; class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { - public static readonly minVersion = API.v220; - public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { return this.getSymbolLocations('implementation', document, position, token); } @@ -21,8 +17,6 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, TypeScriptImplementationProvider.minVersion, () => { - return vscode.languages.registerImplementationProvider(selector, - new TypeScriptImplementationProvider(client)); - }); -} \ No newline at end of file + return vscode.languages.registerImplementationProvider(selector, + new TypeScriptImplementationProvider(client)); +} diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index c732ade5223..eccb5433e76 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -8,8 +8,7 @@ import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; -import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; +import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens, getSymbolRange } from './baseCodeLensProvider'; import { CachedResponse } from '../tsServer/cachedResponse'; import * as typeConverters from '../utils/typeConverters'; @@ -17,7 +16,6 @@ import * as typeConverters from '../utils/typeConverters'; const localize = nls.loadMessageBundle(); export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider { - public static readonly minVersion = API.v220; public async resolveCodeLens( inputCodeLens: vscode.CodeLens, @@ -96,9 +94,8 @@ export function register( client: ITypeScriptServiceClient, cachedResponse: CachedResponse, ) { - return new VersionDependentRegistration(client, TypeScriptImplementationsCodeLensProvider.minVersion, () => - new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { - return vscode.languages.registerCodeLensProvider(selector, - new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); - })); + return new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { + return vscode.languages.registerCodeLensProvider(selector, + new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); + }); } diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 6e4ff1299eb..171f204703e 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -64,7 +64,7 @@ export class TypeScriptServerSpawner { const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager); - if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { + if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { if (tsServerLogFile) { this._logger.info(`<${kind}> Log file: ${tsServerLogFile}`); } else { @@ -103,9 +103,8 @@ export class TypeScriptServerSpawner { currentVersion: TypeScriptVersion, apiVersion: API, pluginManager: PluginManager, - ): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } { + ): { args: string[], cancellationPipeName: string, tsServerLogFile: string | undefined } { const args: string[] = []; - let cancellationPipeName: string | undefined; let tsServerLogFile: string | undefined; if (kind === 'syntax') { @@ -126,12 +125,10 @@ export class TypeScriptServerSpawner { args.push('--enableTelemetry'); } - if (apiVersion.gte(API.v222)) { - cancellationPipeName = electron.getTempFile('tscancellation'); - args.push('--cancellationPipeName', cancellationPipeName + '*'); - } + const cancellationPipeName = electron.getTempFile('tscancellation'); + args.push('--cancellationPipeName', cancellationPipeName + '*'); - if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { + if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { const logDir = this._logDirectoryProvider.getNewLogDirectory(); if (logDir) { tsServerLogFile = path.join(logDir, `tsserver.log`); @@ -140,29 +137,25 @@ export class TypeScriptServerSpawner { } } - if (apiVersion.gte(API.v230)) { - const pluginPaths = this._pluginPathsProvider.getPluginPaths(); + const pluginPaths = this._pluginPathsProvider.getPluginPaths(); - if (pluginManager.plugins.length) { - args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); + if (pluginManager.plugins.length) { + args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); - const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; - for (const plugin of pluginManager.plugins) { - if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { - pluginPaths.push(plugin.path); - } + const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; + for (const plugin of pluginManager.plugins) { + if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { + pluginPaths.push(plugin.path); } } - - if (pluginPaths.length !== 0) { - args.push('--pluginProbeLocations', pluginPaths.join(',')); - } } - if (apiVersion.gte(API.v234)) { - if (configuration.npmLocation) { - args.push('--npmLocation', `"${configuration.npmLocation}"`); - } + if (pluginPaths.length !== 0) { + args.push('--pluginProbeLocations', pluginPaths.join(',')); + } + + if (configuration.npmLocation) { + args.push('--npmLocation', `"${configuration.npmLocation}"`); } if (apiVersion.gte(API.v260)) { @@ -195,9 +188,8 @@ export class TypeScriptServerSpawner { return undefined; } - private static isLoggingEnabled(apiVersion: API, configuration: TypeScriptServiceConfiguration) { - return apiVersion.gte(API.v222) && - configuration.tsServerLogLevel !== TsServerLogLevel.Off; + private static isLoggingEnabled(configuration: TypeScriptServiceConfiguration) { + return configuration.tsServerLogLevel !== TsServerLogLevel.Off; } private static getTsLocale(configuration: TypeScriptServiceConfiguration): string { diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 2571d395a5f..7e0d159132c 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -15,7 +15,6 @@ import LanguageProvider from './languageProvider'; import * as Proto from './protocol'; import * as PConst from './protocol.const'; import TypeScriptServiceClient from './typescriptServiceClient'; -import API from './utils/api'; import { CommandManager } from './utils/commandManager'; import { Disposable } from './utils/dispose'; import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription'; @@ -105,10 +104,6 @@ export default class TypeScriptServiceClientHost extends Disposable { this.client.ensureServiceStarted(); this.client.onReady(() => { - if (this.client.apiVersion.lt(API.v230)) { - return; - } - const languages = new Set(); for (const plugin of pluginManager.plugins) { for (const language of plugin.languages) { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c0f6db7660a..f774fc271d9 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -392,14 +392,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public async openTsServerLogFile(): Promise { - if (this.apiVersion.lt(API.v222)) { - vscode.window.showErrorMessage( - localize( - 'typescript.openTsServerLog.notSupported', - 'TS Server logging requires TS 2.2.2+')); - return false; - } - if (this._configuration.tsServerLogLevel === TsServerLogLevel.Off) { vscode.window.showErrorMessage( localize( diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index b3ce96a8ec5..c30bee16c7e 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -14,10 +14,6 @@ export default class API { } public static readonly defaultVersion = API.fromSimpleString('1.0.0'); - public static readonly v220 = API.fromSimpleString('2.2.0'); - public static readonly v222 = API.fromSimpleString('2.2.2'); - public static readonly v230 = API.fromSimpleString('2.3.0'); - public static readonly v234 = API.fromSimpleString('2.3.4'); public static readonly v240 = API.fromSimpleString('2.4.0'); public static readonly v250 = API.fromSimpleString('2.5.0'); public static readonly v260 = API.fromSimpleString('2.6.0'); From 01f429e8c97d338a47bdd231b284529b43d231bd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 16:27:26 -0700 Subject: [PATCH 60/89] Don't show progress indicator when we know for sure we are not doing a js/ts rename --- .../src/features/updatePathsOnRename.ts | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index fc4e10315ee..993f9d9501f 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -45,12 +45,29 @@ class UpdateImportsOnFileRenameHandler extends Disposable { ) { super(); - this._register(vscode.workspace.onDidRenameFile(e => { + this._register(vscode.workspace.onDidRenameFile(async ({ newUri, oldUri }) => { + const newFilePath = this.client.toPath(newUri); + if (!newFilePath) { + return; + } + + const oldFilePath = this.client.toPath(oldUri); + if (!oldFilePath) { + return; + } + + // Try to get a js/ts file that is being moved + // For directory moves, this returns a js/ts file under the directory. + const jsTsFileThatIsBeingMoved = await this.getJsTsFileBeingMoved(newUri); + if (!jsTsFileThatIsBeingMoved || !this.client.toPath(jsTsFileThatIsBeingMoved)) { + return; + } + vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: localize('renameProgress.title', "Checking for update of JS/TS imports") }, () => { - return this.doRename(e.oldUri, e.newUri); + return this.doRename(oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved); }); })); } @@ -58,24 +75,10 @@ class UpdateImportsOnFileRenameHandler extends Disposable { private async doRename( oldResource: vscode.Uri, newResource: vscode.Uri, + newFilePath: string, + oldFilePath: string, + jsTsFileThatIsBeingMoved: vscode.Uri, ): Promise { - // Try to get a js/ts file that is being moved - // For directory moves, this returns a js/ts file under the directory. - const jsTsFileThatIsBeingMoved = await this.getJsTsFileBeingMoved(newResource); - if (!jsTsFileThatIsBeingMoved || !this.client.toPath(jsTsFileThatIsBeingMoved)) { - return; - } - - const newFilePath = this.client.toPath(newResource); - if (!newFilePath) { - return; - } - - const oldFilePath = this.client.toPath(oldResource); - if (!oldFilePath) { - return; - } - const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); const config = this.getConfiguration(document); From 645a17835324c068edf5e034e1fa1ddc86663031 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 17:09:48 -0700 Subject: [PATCH 61/89] Use destructured paramters --- .../src/features/updatePathsOnRename.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 993f9d9501f..c92d0205cb1 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -35,6 +35,14 @@ const enum UpdateImportsOnFileMoveSetting { Never = 'never', } +interface RenameAction { + readonly oldUri: vscode.Uri; + readonly newUri: vscode.Uri; + readonly newFilePath: string; + readonly oldFilePath: string; + readonly jsTsFileThatIsBeingMoved: vscode.Uri; +} + class UpdateImportsOnFileRenameHandler extends Disposable { public static readonly minVersion = API.v300; @@ -67,18 +75,12 @@ class UpdateImportsOnFileRenameHandler extends Disposable { location: vscode.ProgressLocation.Window, title: localize('renameProgress.title', "Checking for update of JS/TS imports") }, () => { - return this.doRename(oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved); + return this.doRename({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }); }); })); } - private async doRename( - oldResource: vscode.Uri, - newResource: vscode.Uri, - newFilePath: string, - oldFilePath: string, - jsTsFileThatIsBeingMoved: vscode.Uri, - ): Promise { + private async doRename({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }: RenameAction): Promise { const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); const config = this.getConfiguration(document); @@ -88,7 +90,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } // Make sure TS knows about file - this.client.bufferSyncSupport.closeResource(oldResource); + this.client.bufferSyncSupport.closeResource(oldUri); this.client.bufferSyncSupport.openTextDocument(document); const edits = await this.getEditsForFileRename(document, oldFilePath, newFilePath); @@ -96,7 +98,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return; } - if (await this.confirmActionWithUser(newResource, document)) { + if (await this.confirmActionWithUser(newUri, document)) { await vscode.workspace.applyEdit(edits); } } From 6290c3ff0b50c884c72bba62baee95e8e7415855 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 17:17:27 -0700 Subject: [PATCH 62/89] Use file name to determine if we are in a ts file or not Using a textdocument to know if we are in a typescript vs a javascript document is ideal but comes at a performance cost. In most cases we can use the file name safely --- .../src/features/updatePathsOnRename.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index c92d0205cb1..76ef25cd3dd 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -13,7 +13,6 @@ import { nulToken } from '../utils/cancellation'; import { VersionDependentRegistration } from '../utils/dependentRegistration'; import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; -import { isTypeScriptDocument } from '../utils/languageModeIds'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; @@ -43,6 +42,10 @@ interface RenameAction { readonly jsTsFileThatIsBeingMoved: vscode.Uri; } +function doesResourceLookLikeATypeScriptFile(resource: vscode.Uri): boolean { + return /\.tsx?$/i.test(resource.fsPath); +} + class UpdateImportsOnFileRenameHandler extends Disposable { public static readonly minVersion = API.v300; @@ -64,6 +67,12 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return; } + const config = this.getConfiguration(newUri); + const setting = config.get(updateImportsOnFileMoveName); + if (setting === UpdateImportsOnFileMoveSetting.Never) { + return; + } + // Try to get a js/ts file that is being moved // For directory moves, this returns a js/ts file under the directory. const jsTsFileThatIsBeingMoved = await this.getJsTsFileBeingMoved(newUri); @@ -83,12 +92,6 @@ class UpdateImportsOnFileRenameHandler extends Disposable { private async doRename({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }: RenameAction): Promise { const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); - const config = this.getConfiguration(document); - const setting = config.get(updateImportsOnFileMoveName); - if (setting === UpdateImportsOnFileMoveSetting.Never) { - return; - } - // Make sure TS knows about file this.client.bufferSyncSupport.closeResource(oldUri); this.client.bufferSyncSupport.openTextDocument(document); @@ -98,16 +101,13 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return; } - if (await this.confirmActionWithUser(newUri, document)) { + if (await this.confirmActionWithUser(newUri)) { await vscode.workspace.applyEdit(edits); } } - private async confirmActionWithUser( - newResource: vscode.Uri, - newDocument: vscode.TextDocument - ): Promise { - const config = this.getConfiguration(newDocument); + private async confirmActionWithUser(newResource: vscode.Uri): Promise { + const config = this.getConfiguration(newResource); const setting = config.get(updateImportsOnFileMoveName); switch (setting) { case UpdateImportsOnFileMoveSetting.Always: @@ -116,18 +116,15 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return false; case UpdateImportsOnFileMoveSetting.Prompt: default: - return this.promptUser(newResource, newDocument); + return this.promptUser(newResource); } } - private getConfiguration(newDocument: vscode.TextDocument) { - return vscode.workspace.getConfiguration(isTypeScriptDocument(newDocument) ? 'typescript' : 'javascript', newDocument.uri); + private getConfiguration(resource: vscode.Uri) { + return vscode.workspace.getConfiguration(doesResourceLookLikeATypeScriptFile(resource) ? 'typescript' : 'javascript', resource); } - private async promptUser( - newResource: vscode.Uri, - newDocument: vscode.TextDocument - ): Promise { + private async promptUser(newResource: vscode.Uri): Promise { const enum Choice { None = 0, Accept = 1, @@ -173,7 +170,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } case Choice.Always: { - const config = this.getConfiguration(newDocument); + const config = this.getConfiguration(newResource); config.update( updateImportsOnFileMoveName, UpdateImportsOnFileMoveSetting.Always, @@ -182,7 +179,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } case Choice.Never: { - const config = this.getConfiguration(newDocument); + const config = this.getConfiguration(newResource); config.update( updateImportsOnFileMoveName, UpdateImportsOnFileMoveSetting.Never, From e4478d363d7a3054e77e7baa1dfe2d3b7a3cd103 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 17:23:03 -0700 Subject: [PATCH 63/89] Batch renames --- .../src/features/updatePathsOnRename.ts | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 76ef25cd3dd..afd96852a4a 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -15,6 +15,7 @@ import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; +import { Delayer } from '../utils/async'; const localize = nls.loadMessageBundle(); @@ -49,6 +50,9 @@ function doesResourceLookLikeATypeScriptFile(resource: vscode.Uri): boolean { class UpdateImportsOnFileRenameHandler extends Disposable { public static readonly minVersion = API.v300; + private readonly _delayer = new Delayer(50); + private readonly _pendingRenames = new Set(); + public constructor( private readonly client: ITypeScriptServiceClient, private readonly fileConfigurationManager: FileConfigurationManager, @@ -80,29 +84,35 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return; } - vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: localize('renameProgress.title', "Checking for update of JS/TS imports") - }, () => { - return this.doRename({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }); + this._pendingRenames.add({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }); + + this._delayer.trigger(() => { + vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: localize('renameProgress.title', "Checking for update of JS/TS imports") + }, () => this.flushRenames()); }); })); } - private async doRename({ oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved }: RenameAction): Promise { - const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); + private async flushRenames(): Promise { + const renames = Array.from(this._pendingRenames); + this._pendingRenames.clear(); + for (const { oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved } of renames) { + const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); - // Make sure TS knows about file - this.client.bufferSyncSupport.closeResource(oldUri); - this.client.bufferSyncSupport.openTextDocument(document); + // Make sure TS knows about file + this.client.bufferSyncSupport.closeResource(oldUri); + this.client.bufferSyncSupport.openTextDocument(document); - const edits = await this.getEditsForFileRename(document, oldFilePath, newFilePath); - if (!edits || !edits.size) { - return; - } + const edits = await this.getEditsForFileRename(document, oldFilePath, newFilePath); + if (!edits || !edits.size) { + return; + } - if (await this.confirmActionWithUser(newUri)) { - await vscode.workspace.applyEdit(edits); + if (await this.confirmActionWithUser(newUri)) { + await vscode.workspace.applyEdit(edits); + } } } From 639ccb016ffdc7fa1c72f6fe11bfc745210970b7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 17:55:25 -0700 Subject: [PATCH 64/89] Batch renames for js/ts Fixes #76268 --- .../src/features/updatePathsOnRename.ts | 99 ++++++++++++++----- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index afd96852a4a..31cac395ce1 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -98,26 +98,36 @@ class UpdateImportsOnFileRenameHandler extends Disposable { private async flushRenames(): Promise { const renames = Array.from(this._pendingRenames); this._pendingRenames.clear(); - for (const { oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved } of renames) { - const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); + for (const group of this.groupRenames(renames)) { + const edits = new vscode.WorkspaceEdit(); + const resourcesBeingRenamed: vscode.Uri[] = []; - // Make sure TS knows about file - this.client.bufferSyncSupport.closeResource(oldUri); - this.client.bufferSyncSupport.openTextDocument(document); + for (const { oldUri, newUri, newFilePath, oldFilePath, jsTsFileThatIsBeingMoved } of group) { + const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); - const edits = await this.getEditsForFileRename(document, oldFilePath, newFilePath); - if (!edits || !edits.size) { - return; + // Make sure TS knows about file + this.client.bufferSyncSupport.closeResource(oldUri); + this.client.bufferSyncSupport.openTextDocument(document); + + if (await this.withEditsForFileRename(edits, document, oldFilePath, newFilePath)) { + resourcesBeingRenamed.push(newUri); + } } - if (await this.confirmActionWithUser(newUri)) { - await vscode.workspace.applyEdit(edits); + if (edits.size) { + if (await this.confirmActionWithUser(resourcesBeingRenamed)) { + await vscode.workspace.applyEdit(edits); + } } } } - private async confirmActionWithUser(newResource: vscode.Uri): Promise { - const config = this.getConfiguration(newResource); + private async confirmActionWithUser(newResources: readonly vscode.Uri[]): Promise { + if (!newResources.length) { + return false; + } + + const config = this.getConfiguration(newResources[0]); const setting = config.get(updateImportsOnFileMoveName); switch (setting) { case UpdateImportsOnFileMoveSetting.Always: @@ -126,7 +136,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return false; case UpdateImportsOnFileMoveSetting.Prompt: default: - return this.promptUser(newResource); + return this.promptUser(newResources); } } @@ -134,7 +144,11 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return vscode.workspace.getConfiguration(doesResourceLookLikeATypeScriptFile(resource) ? 'typescript' : 'javascript', resource); } - private async promptUser(newResource: vscode.Uri): Promise { + private async promptUser(newResources: readonly vscode.Uri[]): Promise { + if (!newResources.length) { + return false; + } + const enum Choice { None = 0, Accept = 1, @@ -144,11 +158,14 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } interface Item extends vscode.MessageItem { - choice: Choice; + readonly choice: Choice; } + const response = await vscode.window.showInformationMessage( - localize('prompt', "Update imports for moved file: '{0}'?", path.basename(newResource.fsPath)), { + this.getConfirmMessage(newResources.length === 1 + ? localize('prompt', "Update imports for moved file:") + : localize('promptMoreThanOne', "Update imports for moved files:"), newResources), { modal: true, }, { title: localize('reject.title', "No"), @@ -180,7 +197,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } case Choice.Always: { - const config = this.getConfiguration(newResource); + const config = this.getConfiguration(newResources[0]); config.update( updateImportsOnFileMoveName, UpdateImportsOnFileMoveSetting.Always, @@ -189,7 +206,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { } case Choice.Never: { - const config = this.getConfiguration(newResource); + const config = this.getConfiguration(newResources[0]); config.update( updateImportsOnFileMoveName, UpdateImportsOnFileMoveSetting.Never, @@ -217,11 +234,12 @@ class UpdateImportsOnFileRenameHandler extends Disposable { return (await this._handles(resource)) ? resource : undefined; } - private async getEditsForFileRename( + private async withEditsForFileRename( + edits: vscode.WorkspaceEdit, document: vscode.TextDocument, oldFilePath: string, newFilePath: string, - ): Promise { + ): Promise { const response = await this.client.interruptGetErr(() => { this.fileConfigurationManager.setGlobalConfigurationFromDocument(document, nulToken); const args: Proto.GetEditsForFileRenameRequestArgs = { @@ -230,11 +248,46 @@ class UpdateImportsOnFileRenameHandler extends Disposable { }; return this.client.execute('getEditsForFileRename', args, nulToken); }); - if (response.type !== 'response') { - return; + if (response.type !== 'response' || !response.body.length) { + return false; } - return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body); + typeConverters.WorkspaceEdit.withFileCodeEdits(edits, this.client, response.body); + return true; + } + + private groupRenames(renames: Iterable): Iterable> { + const groups = new Map>(); + + for (const rename of renames) { + // Group renames by type (js/ts) and by workspace. + const key = `${this.client.getWorkspaceRootForResource(rename.jsTsFileThatIsBeingMoved)}@@@${doesResourceLookLikeATypeScriptFile(rename.jsTsFileThatIsBeingMoved)}`; + if (!groups.has(key)) { + groups.set(key, new Set()); + } + groups.get(key)!.add(rename); + } + + return groups.values(); + } + + private getConfirmMessage(start: string, resourcesToConfirm: readonly vscode.Uri[]): string { + const MAX_CONFIRM_FILES = 10; + + const paths = [start]; + paths.push(''); + paths.push(...resourcesToConfirm.slice(0, MAX_CONFIRM_FILES).map(r => path.basename(r.fsPath))); + + if (resourcesToConfirm.length > MAX_CONFIRM_FILES) { + if (resourcesToConfirm.length - MAX_CONFIRM_FILES === 1) { + paths.push(localize('moreFile', "...1 additional file not shown")); + } else { + paths.push(localize('moreFiles', "...{0} additional files not shown", resourcesToConfirm.length - MAX_CONFIRM_FILES)); + } + } + + paths.push(''); + return paths.join('\n'); } } From 8c0e291789a71d93cf61c42fa7727c7c381e2bdd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 18:04:22 -0700 Subject: [PATCH 65/89] Make message for update imports better match vscode's message For #76268 --- .../src/features/updatePathsOnRename.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 31cac395ce1..35fbd6ce92b 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -163,9 +163,9 @@ class UpdateImportsOnFileRenameHandler extends Disposable { const response = await vscode.window.showInformationMessage( - this.getConfirmMessage(newResources.length === 1 - ? localize('prompt', "Update imports for moved file:") - : localize('promptMoreThanOne', "Update imports for moved files:"), newResources), { + newResources.length === 1 + ? localize('prompt', "Update imports for '{0}'?", path.basename(newResources[0].fsPath)) + : this.getConfirmMessage(localize('promptMoreThanOne', "Update imports for the following {0} files?", newResources.length), newResources), { modal: true, }, { title: localize('reject.title', "No"), From 21bc76fdf9cd73f595524692b246a2fed1758d8e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 23 Oct 2019 18:17:59 -0700 Subject: [PATCH 66/89] Fix context menu appearing on web when right clicking terminal Happens when copyPaste is the rightClickBehavior --- src/vs/workbench/contrib/terminal/browser/terminalPanel.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index b2893eacdcf..26a64d78bec 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -253,6 +253,7 @@ export class TerminalPanel extends Panel { getActionsContext: () => this._parentDomElement }); } else { + event.preventDefault(); event.stopImmediatePropagation(); } this._cancelContextMenu = false; From 4d4040c3d154761eb8f4949de1b93c4692ec1eee Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 18:24:18 -0700 Subject: [PATCH 67/89] Avoid cast --- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 872fd2be5e8..a5378684bd5 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -348,7 +348,7 @@ class CodeActionAdapter { only: context.only ? new CodeActionKind(context.only) : undefined }; - return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => { + return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then((commandsOrActions): extHostProtocol.ICodeActionListDto | undefined => { if (!isNonEmptyArray(commandsOrActions) || token.isCancellationRequested) { return undefined; } @@ -390,7 +390,7 @@ class CodeActionAdapter { } } - return { cacheId, actions }; + return { cacheId, actions }; }); } From da891e5264a144b336675cca88b19d789ea17686 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 19:30:05 -0700 Subject: [PATCH 68/89] Remove log --- src/vs/workbench/contrib/webview/browser/webviewElement.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 3745c6c67df..cf6eac444d4 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -108,7 +108,6 @@ export class IFrameWebview extends BaseWebview implements Web } focus(): void { - console.log('focus'); if (this.element) { this._send('focus'); } From af4ea4d46a4936f90048e8bd79f6455f46fc47c9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 19:37:19 -0700 Subject: [PATCH 69/89] Make sure we render border in split webviews --- src/vs/workbench/contrib/customEditor/browser/customEditors.ts | 2 +- .../contrib/webview/browser/dynamicWebviewEditorOverlay.ts | 2 +- src/vs/workbench/contrib/webview/browser/webviewElement.ts | 2 +- .../contrib/webview/electron-browser/webviewElement.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 5cc01a28ffa..e9d1440016c 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -180,7 +180,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): CustomFileEditorInput { const id = generateUuid(); const webview = new Lazy(() => { - return new UnownedDisposable(this.webviewService.createWebviewEditorOverlay(id, { customClasses: options ? options.customClasses : undefined }, {})); + return new UnownedDisposable(this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {})); }); const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, webview); if (group) { diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index d5365f700f3..945e6be9f4b 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -143,7 +143,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd } public get options(): WebviewOptions { return this._options; } - public set options(value: WebviewOptions) { this._options = value; } + public set options(value: WebviewOptions) { this._options = { customClasses: this._options.customClasses, ...value }; } public get contentOptions(): WebviewContentOptions { return this._contentOptions; } public set contentOptions(value: WebviewContentOptions) { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index cf6eac444d4..d4926d849b4 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -60,7 +60,7 @@ export class IFrameWebview extends BaseWebview implements Web protected createElement(options: WebviewOptions) { const element = document.createElement('iframe'); - element.className = `webview ${options.customClasses}`; + element.className = `webview ${options.customClasses || ''}`; element.sandbox.add('allow-scripts', 'allow-same-origin'); element.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}`); element.style.border = 'none'; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 270746b76c8..929b367097b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -296,7 +296,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme const element = document.createElement('webview'); element.setAttribute('partition', `webview${Date.now()}`); element.setAttribute('webpreferences', 'contextIsolation=yes'); - element.className = `webview ${options.customClasses}`; + element.className = `webview ${options.customClasses || ''}`; element.style.flex = '0 1'; element.style.width = '0'; From 78559bf9e6d6a5e7cd2d5331b19ee50054cf217f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Oct 2019 20:27:52 -0700 Subject: [PATCH 70/89] Normalize vscode links in markdown files to target current vscode version Fixes #71622 This allows you to use generic `vscode` links inside the markdown preview and still have them work if the preview is viewed in insiders --- extensions/markdown-language-features/media/index.js | 4 ++-- .../markdown-language-features/preview-src/index.ts | 2 +- .../src/features/documentLinkProvider.ts | 6 +++++- .../markdown-language-features/src/markdownEngine.ts | 11 ++++++++++- .../markdown-language-features/src/util/links.ts | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 666f4394dbb..19817eb3706 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -1,2 +1,2 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function i(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=i,t.getSettings=function(){if(o)return o;if(o=i("data-settings"))return o;throw new Error("Could not load settings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);function i(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const r=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName("code-line")){const n=+t.getAttribute("data-line");isNaN(n)||e.push({element:t,line:n})}}return e}})();function s(e){const t=Math.floor(e),n=r();let o=n[0]||null;for(const e of n){if(e.line===t)return{previous:e,next:void 0};if(e.line>t)return{previous:o,next:e};o=e}return{previous:o}}function c(e){const t=r(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1=n?i=e:o=e}const s=t[i],c=s.element.getBoundingClientRect();if(i>=1&&c.top>n){return{previous:t[o],next:s}}return{previous:s}}t.getElementsForSourceLine=s,t.getLineElementsAtPageOffset=c,t.scrollToRevealSourceLine=function(e){if(!o.getSettings().scrollPreviewWithEditor)return;if(e<=0)return void window.scroll(window.scrollX,0);const{previous:t,next:n}=s(e);if(!t)return;let i=0;const r=t.element.getBoundingClientRect(),c=r.top;if(n&&n.line!==t.line)i=c+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-c);else{const t=e-Math.floor(e);i=c+r.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+i))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=c(e);if(t){const o=t.element.getBoundingClientRect(),r=e-window.scrollY-o.top;if(n){const e=r/(n.element.getBoundingClientRect().top-o.top);return i(t.line+e*(n.line-t.line))}{const e=r/o.height;return i(t.line+e)}}return null},t.getLineElementForFragment=function(e){return r().find(t=>t.element.id===e)}},,,,,function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){(function(t){var n="Expected a function",o=NaN,i="[object Symbol]",r=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,d="object"==typeof self&&self&&self.Object===Object&&self,f=l||d||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return f.Date.now()};function h(e,t,o){var i,r,s,c,a,u,l=0,d=!1,f=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=i,o=r;return i=r=void 0,l=t,c=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||f&&e-l>=s}function E(){var e=v();if(y(e))return L(e);a=setTimeout(E,function(e){var n=t-(e-u);return f?m(n,s-(e-l)):n}(e))}function L(e){return a=void 0,g&&i?h(e):(i=r=void 0,c)}function M(){var e=v(),n=y(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(E,t),d?h(e):c}(u);if(f)return a=setTimeout(E,t),h(u)}return void 0===a&&(a=setTimeout(E,t)),c}return t=b(t)||0,w(o)&&(d=!!o.leading,s=(f="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),M.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},M.flush=function(){return void 0===a?c:L(v())},M}function w(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function b(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&g.call(e)==i}(e))return o;if(w(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=w(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(r,"");var n=c.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),h(e,t,{leading:i,maxWait:t,trailing:r})}}).call(this,n(6))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);t.createPosterForVsCode=(e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1);t.ActiveLineMarker=class{onDidChangeTextEditorSelection(e){const{previous:t}=o.getElementsForSourceLine(e);this._update(t&&t.element)}_update(e){this._unmarkActiveElement(this._current),this._markActiveElement(e),this._current=e}_unmarkActiveElement(e){e&&(e.className=e.className.replace(/\bcode-active-line\b/g,""))}_markActiveElement(e){e&&(e.className+=" code-active-line")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(10),i=n(9),r=n(8),s=n(1),c=n(0),a=n(7);let u=!0;const l=new o.ActiveLineMarker,d=c.getSettings(),f=acquireVsCodeApi();let g=c.getData("data-state");f.setState(g);const p=r.createPosterForVsCode(f);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=(()=>{v()}),i.onceDocumentLoaded(()=>{d.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+d.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=a(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.line=t,e(t))}})();let v=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n{u=!0,v()},!0),window.addEventListener("message",e=>{if(e.data.source===d.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,d)}},!1),document.addEventListener("dblclick",e=>{if(!d.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=s.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||p.postMessage("didClick",{line:Math.floor(n)})});const h=["http:","https:","mailto:","vscode:","vscode-insiders"];document.addEventListener("click",e=>{if(!e)return;let t=e.target;for(;t;){if(t.tagName&&"A"===t.tagName&&t.href){if(t.getAttribute("href").startsWith("#"))return;if(h.some(e=>t.href.startsWith(e)))return;const n=t.getAttribute("data-href")||t.getAttribute("href");return/^[a-z\-]+:/i.test(n)?void 0:(p.postMessage("openLink",{href:n}),e.preventDefault(),void e.stopPropagation())}t=t.parentNode}},!0),window.addEventListener("scroll",a(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,f.setState(g))}},50))}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9kYXNoLnRocm90dGxlL2luZGV4LmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9pbmRleC50cyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiY29uZmlndXJhYmxlIiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJ2YWx1ZSIsIm4iLCJfX2VzTW9kdWxlIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY2FjaGVkU2V0dGluZ3MiLCJ1bmRlZmluZWQiLCJnZXREYXRhIiwia2V5IiwiZWxlbWVudCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJkYXRhIiwiZ2V0QXR0cmlidXRlIiwiSlNPTiIsInBhcnNlIiwiRXJyb3IiLCJnZXRTZXR0aW5ncyIsInNldHRpbmdzXzEiLCJjbGFtcExpbmUiLCJsaW5lIiwibWluIiwibWF4IiwibGluZUNvdW50IiwiTWF0aCIsImdldENvZGVMaW5lRWxlbWVudHMiLCJlbGVtZW50cyIsImJvZHkiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiaXNOYU4iLCJwdXNoIiwiZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lIiwidGFyZ2V0TGluZSIsImxpbmVOdW1iZXIiLCJmbG9vciIsImxpbmVzIiwicHJldmlvdXMiLCJlbnRyeSIsIm5leHQiLCJnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQiLCJvZmZzZXQiLCJwb3NpdGlvbiIsIndpbmRvdyIsInNjcm9sbFkiLCJsbyIsImhpIiwibGVuZ3RoIiwibWlkIiwiYm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiZyIsInRoaXMiLCJGdW5jdGlvbiIsImV2YWwiLCJlIiwiZ2xvYmFsIiwiRlVOQ19FUlJPUl9URVhUIiwiTkFOIiwic3ltYm9sVGFnIiwicmVUcmltIiwicmVJc0JhZEhleCIsInJlSXNCaW5hcnkiLCJyZUlzT2N0YWwiLCJmcmVlUGFyc2VJbnQiLCJwYXJzZUludCIsImZyZWVHbG9iYWwiLCJmcmVlU2VsZiIsInNlbGYiLCJyb290Iiwib2JqZWN0VG9TdHJpbmciLCJ0b1N0cmluZyIsIm5hdGl2ZU1heCIsIm5hdGl2ZU1pbiIsIm5vdyIsIkRhdGUiLCJkZWJvdW5jZSIsImZ1bmMiLCJ3YWl0Iiwib3B0aW9ucyIsImxhc3RBcmdzIiwibGFzdFRoaXMiLCJtYXhXYWl0IiwicmVzdWx0IiwidGltZXJJZCIsImxhc3RDYWxsVGltZSIsImxhc3RJbnZva2VUaW1lIiwibGVhZGluZyIsIm1heGluZyIsInRyYWlsaW5nIiwiVHlwZUVycm9yIiwiaW52b2tlRnVuYyIsInRpbWUiLCJhcmdzIiwidGhpc0FyZyIsImFwcGx5Iiwic2hvdWxkSW52b2tlIiwidGltZVNpbmNlTGFzdENhbGwiLCJ0aW1lckV4cGlyZWQiLCJ0cmFpbGluZ0VkZ2UiLCJzZXRUaW1lb3V0IiwicmVtYWluaW5nV2FpdCIsImRlYm91bmNlZCIsImlzSW52b2tpbmciLCJhcmd1bWVudHMiLCJsZWFkaW5nRWRnZSIsInRvTnVtYmVyIiwiaXNPYmplY3QiLCJjYW5jZWwiLCJjbGVhclRpbWVvdXQiLCJmbHVzaCIsInR5cGUiLCJpc09iamVjdExpa2UiLCJpc1N5bWJvbCIsIm90aGVyIiwidmFsdWVPZiIsInJlcGxhY2UiLCJpc0JpbmFyeSIsInRlc3QiLCJzbGljZSIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsInZzY29kZSIsIltvYmplY3QgT2JqZWN0XSIsInBvc3RNZXNzYWdlIiwic291cmNlIiwib25jZURvY3VtZW50TG9hZGVkIiwiZiIsInJlYWR5U3RhdGUiLCJhZGRFdmVudExpc3RlbmVyIiwic2Nyb2xsX3N5bmNfMSIsIkFjdGl2ZUxpbmVNYXJrZXIiLCJfdXBkYXRlIiwiYmVmb3JlIiwiX3VubWFya0FjdGl2ZUVsZW1lbnQiLCJfY3VycmVudCIsIl9tYXJrQWN0aXZlRWxlbWVudCIsImNsYXNzTmFtZSIsImFjdGl2ZUxpbmVNYXJrZXJfMSIsImV2ZW50c18xIiwibWVzc2FnaW5nXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwic2V0dGluZ3MiLCJhY3F1aXJlVnNDb2RlQXBpIiwic3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsImluaXRpYWxMaW5lIiwib25VcGRhdGVWaWV3IiwiZG9TY3JvbGwiLCJpbWFnZUluZm8iLCJpbWFnZXMiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImltZyIsImNsYXNzTGlzdCIsImNvbnRhaW5zIiwicmVtb3ZlIiwid2lkdGgiLCJldmVudCIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwidGFnTmFtZSIsInBhZ2VZIiwicGFzc1Rocm91Z2hMaW5rU2NoZW1lcyIsImhyZWYiLCJzdGFydHNXaXRoIiwic29tZSIsInNjaGVtZSIsImhyZWZUZXh0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iXSwibWFwcGluZ3MiOiJhQUNBLElBQUFBLEtBR0EsU0FBQUMsRUFBQUMsR0FHQSxHQUFBRixFQUFBRSxHQUNBLE9BQUFGLEVBQUFFLEdBQUFDLFFBR0EsSUFBQUMsRUFBQUosRUFBQUUsSUFDQUcsRUFBQUgsRUFDQUksR0FBQSxFQUNBSCxZQVVBLE9BTkFJLEVBQUFMLEdBQUFNLEtBQUFKLEVBQUFELFFBQUFDLElBQUFELFFBQUFGLEdBR0FHLEVBQUFFLEdBQUEsRUFHQUYsRUFBQUQsUUFLQUYsRUFBQVEsRUFBQUYsRUFHQU4sRUFBQVMsRUFBQVYsRUFHQUMsRUFBQVUsRUFBQSxTQUFBUixFQUFBUyxFQUFBQyxHQUNBWixFQUFBYSxFQUFBWCxFQUFBUyxJQUNBRyxPQUFBQyxlQUFBYixFQUFBUyxHQUNBSyxjQUFBLEVBQ0FDLFlBQUEsRUFDQUMsSUFBQU4sS0FNQVosRUFBQW1CLEVBQUEsU0FBQWpCLEdBQ0FZLE9BQUFDLGVBQUFiLEVBQUEsY0FBaURrQixPQUFBLEtBSWpEcEIsRUFBQXFCLEVBQUEsU0FBQWxCLEdBQ0EsSUFBQVMsRUFBQVQsS0FBQW1CLFdBQ0EsV0FBMkIsT0FBQW5CLEVBQUEsU0FDM0IsV0FBaUMsT0FBQUEsR0FFakMsT0FEQUgsRUFBQVUsRUFBQUUsRUFBQSxJQUFBQSxHQUNBQSxHQUlBWixFQUFBYSxFQUFBLFNBQUFVLEVBQUFDLEdBQXNELE9BQUFWLE9BQUFXLFVBQUFDLGVBQUFuQixLQUFBZ0IsRUFBQUMsSUFHdER4QixFQUFBMkIsRUFBQSxHQUlBM0IsSUFBQTRCLEVBQUEsbUNDOURBZCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxJQUFBUyxPQUFBQyxFQUNBLFNBQUFDLEVBQUFDLEdBQ0EsTUFBQUMsRUFBQUMsU0FBQUMsZUFBQSxnQ0FDQSxHQUFBRixFQUFBLENBQ0EsTUFBQUcsRUFBQUgsRUFBQUksYUFBQUwsR0FDQSxHQUFBSSxFQUNBLE9BQUFFLEtBQUFDLE1BQUFILEdBR0EsVUFBQUksaUNBQStDUixLQUUvQzlCLEVBQUE2QixVQVdBN0IsRUFBQXVDLFlBVkEsV0FDQSxHQUFBWixFQUNBLE9BQUFBLEVBR0EsR0FEQUEsRUFBQUUsRUFBQSxpQkFFQSxPQUFBRixFQUVBLFVBQUFXLE1BQUEsMERDckJBMUIsT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFDOUMsTUFBQXNCLEVBQUExQyxFQUFBLEdBSUEsU0FBQTJDLEVBQUFDLEdBQ0EsT0FKQUMsRUFJQSxFQUpBQyxFQUlBSixFQUFBRCxjQUFBTSxVQUFBLEVBSkEzQixFQUlBd0IsRUFIQUksS0FBQUgsSUFBQUMsRUFBQUUsS0FBQUYsSUFBQUQsRUFBQXpCLElBREEsSUFBQXlCLEVBQUFDLEVBQUExQixFQU1BLE1BQUE2QixFQUFBLE1BQ0EsSUFBQUMsRUFDQSxXQUNBLElBQUFBLEVBQUEsQ0FDQUEsSUFBeUJqQixRQUFBQyxTQUFBaUIsS0FBQVAsS0FBQSxJQUN6QixVQUFBWCxLQUFBQyxTQUFBa0IsdUJBQUEsY0FDQSxNQUFBUixHQUFBWCxFQUFBSSxhQUFBLGFBQ0FnQixNQUFBVCxJQUNBTSxFQUFBSSxNQUFtQ3JCLFVBQUFXLFVBSW5DLE9BQUFNLElBWkEsR0FxQkEsU0FBQUssRUFBQUMsR0FDQSxNQUFBQyxFQUFBVCxLQUFBVSxNQUFBRixHQUNBRyxFQUFBVixJQUNBLElBQUFXLEVBQUFELEVBQUEsU0FDQSxVQUFBRSxLQUFBRixFQUFBLENBQ0EsR0FBQUUsRUFBQWpCLE9BQUFhLEVBQ0EsT0FBb0JHLFNBQUFDLEVBQUFDLFVBQUFoQyxHQUVwQixHQUFBK0IsRUFBQWpCLEtBQUFhLEVBQ0EsT0FBb0JHLFdBQUFFLEtBQUFELEdBRXBCRCxFQUFBQyxFQUVBLE9BQVlELFlBTVosU0FBQUcsRUFBQUMsR0FDQSxNQUFBTCxFQUFBVixJQUNBZ0IsRUFBQUQsRUFBQUUsT0FBQUMsUUFDQSxJQUFBQyxHQUFBLEVBQ0FDLEVBQUFWLEVBQUFXLE9BQUEsRUFDQSxLQUFBRixFQUFBLEVBQUFDLEdBQUEsQ0FDQSxNQUFBRSxFQUFBdkIsS0FBQVUsT0FBQVUsRUFBQUMsR0FBQSxHQUNBRyxFQUFBYixFQUFBWSxHQUFBdEMsUUFBQXdDLHdCQUNBRCxFQUFBRSxJQUFBRixFQUFBRyxRQUFBVixFQUNBSSxFQUFBRSxFQUdBSCxFQUFBRyxFQUdBLE1BQUFLLEVBQUFqQixFQUFBVSxHQUNBUSxFQUFBRCxFQUFBM0MsUUFBQXdDLHdCQUNBLEdBQUFKLEdBQUEsR0FBQVEsRUFBQUgsSUFBQVQsRUFBQSxDQUVBLE9BQWdCTCxTQURoQkQsRUFBQVMsR0FDZ0JOLEtBQUFjLEdBRWhCLE9BQVloQixTQUFBZ0IsR0F6QloxRSxFQUFBcUQsMkJBMkJBckQsRUFBQTZELDhCQStCQTdELEVBQUE0RSx5QkEzQkEsU0FBQWxDLEdBQ0EsSUFBQUYsRUFBQUQsY0FBQXNDLHdCQUNBLE9BRUEsR0FBQW5DLEdBQUEsRUFFQSxZQURBc0IsT0FBQWMsT0FBQWQsT0FBQWUsUUFBQSxHQUdBLE1BQUFyQixTQUFXQSxFQUFBRSxRQUFpQlAsRUFBQVgsR0FDNUIsSUFBQWdCLEVBQ0EsT0FFQSxJQUFBc0IsRUFBQSxFQUNBLE1BQUFDLEVBQUF2QixFQUFBM0IsUUFBQXdDLHdCQUNBVyxFQUFBRCxFQUFBVCxJQUNBLEdBQUFaLEtBQUFsQixPQUFBZ0IsRUFBQWhCLEtBSUFzQyxFQUFBRSxHQUZBeEMsRUFBQWdCLEVBQUFoQixPQUFBa0IsRUFBQWxCLEtBQUFnQixFQUFBaEIsT0FDQWtCLEVBQUE3QixRQUFBd0Msd0JBQUFDLElBQUFVLE9BR0EsQ0FDQSxNQUFBQyxFQUFBekMsRUFBQUksS0FBQVUsTUFBQWQsR0FDQXNDLEVBQUFFLEVBQUFELEVBQUFSLE9BQUFVLEVBRUFuQixPQUFBYyxPQUFBZCxPQUFBZSxRQUFBakMsS0FBQUYsSUFBQSxFQUFBb0IsT0FBQUMsUUFBQWUsS0FxQkFoRixFQUFBb0YsaUNBbEJBLFNBQUF0QixHQUNBLE1BQUFKLFNBQVdBLEVBQUFFLFFBQWlCQyxFQUFBQyxHQUM1QixHQUFBSixFQUFBLENBQ0EsTUFBQTJCLEVBQUEzQixFQUFBM0IsUUFBQXdDLHdCQUNBZSxFQUFBeEIsRUFBQUUsT0FBQUMsUUFBQW9CLEVBQUFiLElBQ0EsR0FBQVosRUFBQSxDQUNBLE1BQUEyQixFQUFBRCxHQUFBMUIsRUFBQTdCLFFBQUF3Qyx3QkFBQUMsSUFBQWEsRUFBQWIsS0FFQSxPQUFBL0IsRUFEQWlCLEVBQUFoQixLQUFBNkMsR0FBQTNCLEVBQUFsQixLQUFBZ0IsRUFBQWhCLE9BR0EsQ0FDQSxNQUFBOEMsRUFBQUYsRUFBQUQsRUFBQSxPQUVBLE9BQUE1QyxFQURBaUIsRUFBQWhCLEtBQUE4QyxJQUlBLGFBV0F4RixFQUFBeUYsMEJBTEEsU0FBQUMsR0FDQSxPQUFBM0MsSUFBQTRDLEtBQUE1RCxHQUNBQSxVQUFBNkQsS0FBQUYsdUJDcElBLElBQUFHLEVBR0FBLEVBQUEsV0FDQSxPQUFBQyxLQURBLEdBSUEsSUFFQUQsS0FBQUUsU0FBQSxjQUFBQSxLQUFBLEVBQUFDLE1BQUEsUUFDQyxNQUFBQyxHQUVELGlCQUFBakMsU0FBQTZCLEVBQUE3QixRQU9BL0QsRUFBQUQsUUFBQTZGLG9CQ25CQSxTQUFBSyxHQVVBLElBQUFDLEVBQUEsc0JBR0FDLEVBQUEsSUFHQUMsRUFBQSxrQkFHQUMsRUFBQSxhQUdBQyxFQUFBLHFCQUdBQyxFQUFBLGFBR0FDLEVBQUEsY0FHQUMsRUFBQUMsU0FHQUMsRUFBQSxpQkFBQVYsUUFBQXRGLGlCQUFBc0YsRUFHQVcsRUFBQSxpQkFBQUMsaUJBQUFsRyxpQkFBQWtHLEtBR0FDLEVBQUFILEdBQUFDLEdBQUFkLFNBQUEsY0FBQUEsR0FVQWlCLEVBUEFwRyxPQUFBVyxVQU9BMEYsU0FHQUMsRUFBQXBFLEtBQUFGLElBQ0F1RSxFQUFBckUsS0FBQUgsSUFrQkF5RSxFQUFBLFdBQ0EsT0FBQUwsRUFBQU0sS0FBQUQsT0F5REEsU0FBQUUsRUFBQUMsRUFBQUMsRUFBQUMsR0FDQSxJQUFBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUFBLEVBQ0FDLEdBQUEsRUFDQUMsR0FBQSxFQUNBQyxHQUFBLEVBRUEsc0JBQUFaLEVBQ0EsVUFBQWEsVUFBQWpDLEdBVUEsU0FBQWtDLEVBQUFDLEdBQ0EsSUFBQUMsRUFBQWIsRUFDQWMsRUFBQWIsRUFLQSxPQUhBRCxFQUFBQyxPQUFBL0YsRUFDQW9HLEVBQUFNLEVBQ0FULEVBQUFOLEVBQUFrQixNQUFBRCxFQUFBRCxHQXFCQSxTQUFBRyxFQUFBSixHQUNBLElBQUFLLEVBQUFMLEVBQUFQLEVBTUEsWUFBQW5HLElBQUFtRyxHQUFBWSxHQUFBbkIsR0FDQW1CLEVBQUEsR0FBQVQsR0FOQUksRUFBQU4sR0FNQUosRUFHQSxTQUFBZ0IsSUFDQSxJQUFBTixFQUFBbEIsSUFDQSxHQUFBc0IsRUFBQUosR0FDQSxPQUFBTyxFQUFBUCxHQUdBUixFQUFBZ0IsV0FBQUYsRUF6QkEsU0FBQU4sR0FDQSxJQUVBVCxFQUFBTCxHQUZBYyxFQUFBUCxHQUlBLE9BQUFHLEVBQUFmLEVBQUFVLEVBQUFELEdBSEFVLEVBQUFOLElBR0FILEVBb0JBa0IsQ0FBQVQsSUFHQSxTQUFBTyxFQUFBUCxHQUtBLE9BSkFSLE9BQUFsRyxFQUlBdUcsR0FBQVQsRUFDQVcsRUFBQUMsSUFFQVosRUFBQUMsT0FBQS9GLEVBQ0FpRyxHQWVBLFNBQUFtQixJQUNBLElBQUFWLEVBQUFsQixJQUNBNkIsRUFBQVAsRUFBQUosR0FNQSxHQUpBWixFQUFBd0IsVUFDQXZCLEVBQUE3QixLQUNBaUMsRUFBQU8sRUFFQVcsRUFBQSxDQUNBLFFBQUFySCxJQUFBa0csRUFDQSxPQXZFQSxTQUFBUSxHQU1BLE9BSkFOLEVBQUFNLEVBRUFSLEVBQUFnQixXQUFBRixFQUFBcEIsR0FFQVMsRUFBQUksRUFBQUMsR0FBQVQsRUFpRUFzQixDQUFBcEIsR0FFQSxHQUFBRyxFQUdBLE9BREFKLEVBQUFnQixXQUFBRixFQUFBcEIsR0FDQWEsRUFBQU4sR0FNQSxZQUhBbkcsSUFBQWtHLElBQ0FBLEVBQUFnQixXQUFBRixFQUFBcEIsSUFFQUssRUFJQSxPQXhHQUwsRUFBQTRCLEVBQUE1QixJQUFBLEVBQ0E2QixFQUFBNUIsS0FDQVEsSUFBQVIsRUFBQVEsUUFFQUwsR0FEQU0sRUFBQSxZQUFBVCxHQUNBUCxFQUFBa0MsRUFBQTNCLEVBQUFHLFVBQUEsRUFBQUosR0FBQUksRUFDQU8sRUFBQSxhQUFBVixNQUFBVSxZQWlHQWEsRUFBQU0sT0FuQ0EsZ0JBQ0ExSCxJQUFBa0csR0FDQXlCLGFBQUF6QixHQUVBRSxFQUFBLEVBQ0FOLEVBQUFLLEVBQUFKLEVBQUFHLE9BQUFsRyxHQStCQW9ILEVBQUFRLE1BNUJBLFdBQ0EsWUFBQTVILElBQUFrRyxFQUFBRCxFQUFBZ0IsRUFBQXpCLE1BNEJBNEIsRUEwRkEsU0FBQUssRUFBQW5JLEdBQ0EsSUFBQXVJLFNBQUF2SSxFQUNBLFFBQUFBLElBQUEsVUFBQXVJLEdBQUEsWUFBQUEsR0E0RUEsU0FBQUwsRUFBQWxJLEdBQ0Esb0JBQUFBLEVBQ0EsT0FBQUEsRUFFQSxHQWhDQSxTQUFBQSxHQUNBLHVCQUFBQSxHQXRCQSxTQUFBQSxHQUNBLFFBQUFBLEdBQUEsaUJBQUFBLEVBc0JBd0ksQ0FBQXhJLElBQUE4RixFQUFBM0csS0FBQWEsSUFBQW1GLEVBOEJBc0QsQ0FBQXpJLEdBQ0EsT0FBQWtGLEVBRUEsR0FBQWlELEVBQUFuSSxHQUFBLENBQ0EsSUFBQTBJLEVBQUEsbUJBQUExSSxFQUFBMkksUUFBQTNJLEVBQUEySSxVQUFBM0ksRUFDQUEsRUFBQW1JLEVBQUFPLEtBQUEsR0FBQUEsRUFFQSxvQkFBQTFJLEVBQ0EsV0FBQUEsT0FFQUEsSUFBQTRJLFFBQUF4RCxFQUFBLElBQ0EsSUFBQXlELEVBQUF2RCxFQUFBd0QsS0FBQTlJLEdBQ0EsT0FBQTZJLEdBQUF0RCxFQUFBdUQsS0FBQTlJLEdBQ0F3RixFQUFBeEYsRUFBQStJLE1BQUEsR0FBQUYsRUFBQSxLQUNBeEQsRUFBQXlELEtBQUE5SSxHQUFBa0YsR0FBQWxGLEVBR0FqQixFQUFBRCxRQTlJQSxTQUFBdUgsRUFBQUMsRUFBQUMsR0FDQSxJQUFBUSxHQUFBLEVBQ0FFLEdBQUEsRUFFQSxzQkFBQVosRUFDQSxVQUFBYSxVQUFBakMsR0FNQSxPQUpBa0QsRUFBQTVCLEtBQ0FRLEVBQUEsWUFBQVIsTUFBQVEsVUFDQUUsRUFBQSxhQUFBVixNQUFBVSxZQUVBYixFQUFBQyxFQUFBQyxHQUNBUyxVQUNBTCxRQUFBSixFQUNBVyw4RENqVEF2SCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxNQUFBc0IsRUFBQTFDLEVBQUEsR0FDQUUsRUFBQWtLLHNCQUFBLENBQUFDLEdBQ0EsVUFDQUMsWUFBQVgsRUFBQXhHLEdBQ0FrSCxFQUFBRSxhQUNBWixPQUNBYSxPQUFBOUgsRUFBQUQsY0FBQStILE9BQ0FySCwwQ0NSQXJDLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBUzlDbEIsRUFBQXVLLG1CQVJBLFNBQUFDLEdBQ0EsWUFBQXhJLFNBQUF5SSxZQUFBLGtCQUFBekksU0FBQXlJLFdBQ0F6SSxTQUFBMEksaUJBQUEsbUJBQUFGLEdBR0FBLG1DQ1ZBNUosT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFLOUMsTUFBQXlKLEVBQUE3SyxFQUFBLEdBd0JBRSxFQUFBNEssdUJBdEJBUiwrQkFBQTFILEdBQ0EsTUFBQWdCLFNBQWVBLEdBQVdpSCxFQUFBdEgseUJBQUFYLEdBQzFCb0QsS0FBQStFLFFBQUFuSCxLQUFBM0IsU0FFQXFJLFFBQUFVLEdBQ0FoRixLQUFBaUYscUJBQUFqRixLQUFBa0YsVUFDQWxGLEtBQUFtRixtQkFBQUgsR0FDQWhGLEtBQUFrRixTQUFBRixFQUVBVixxQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixVQUFBbkosRUFBQW1KLFVBQUFwQixRQUFBLDZCQUVBTSxtQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixXQUFBLHFEQ3RCQXRLLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBQzlDLE1BQUFpSyxFQUFBckwsRUFBQSxJQUNBc0wsRUFBQXRMLEVBQUEsR0FDQXVMLEVBQUF2TCxFQUFBLEdBQ0E2SyxFQUFBN0ssRUFBQSxHQUNBMEMsRUFBQTFDLEVBQUEsR0FDQXdMLEVBQUF4TCxFQUFBLEdBQ0EsSUFBQXlMLEdBQUEsRUFDQSxNQUFBQyxFQUFBLElBQUFMLEVBQUFQLGlCQUNBYSxFQUFBakosRUFBQUQsY0FDQTRILEVBQUF1QixtQkFFQSxJQUFBQyxFQUFBbkosRUFBQVgsUUFBQSxjQUNBc0ksRUFBQXlCLFNBQUFELEdBQ0EsTUFBQUUsRUFBQVIsRUFBQW5CLHNCQUFBQyxHQUNBbkcsT0FBQThILFdBQUFDLFVBQUFGLEdBQ0E3SCxPQUFBZ0ksb0JBQUFELFVBQUFGLEdBQ0E3SCxPQUFBaUksT0FBQSxNQUNBQyxNQUVBZCxFQUFBYixtQkFBQSxLQUNBa0IsRUFBQTVHLHlCQUNBaUUsV0FBQSxLQUVBLEdBQUE2QyxFQUFBakcsU0FBQSxDQUNBLE1BQUEzRCxFQUFBNEksRUFBQWxGLDBCQUFBa0csRUFBQWpHLFVBQ0EzRCxJQUNBd0osR0FBQSxFQUNBWixFQUFBL0YseUJBQUE3QyxFQUFBVyxXQUdBLENBQ0EsTUFBQXlKLEdBQUFWLEVBQUEvSSxLQUNBUyxNQUFBZ0osS0FDQVosR0FBQSxFQUNBWixFQUFBL0YseUJBQUF1SCxNQUdTLEtBR1QsTUFBQUMsRUFBQSxNQUNBLE1BQUFDLEVBQUFmLEVBQUE1SSxJQUNBNkksR0FBQSxFQUNBWixFQUFBL0YseUJBQUFsQyxJQUNLLElBQ0wsT0FBQUEsRUFBQStJLEtBQ0F0SSxNQUFBVCxLQUNBK0ksRUFBQS9JLE9BQ0EySixFQUFBM0osTUFSQSxHQVlBLElBQUF3SixFQUFBWixFQUFBLEtBQ0EsTUFBQWdCLEtBQ0EsSUFBQUMsRUFBQXZLLFNBQUF3SyxxQkFBQSxPQUNBLEdBQUFELEVBQUEsQ0FDQSxJQUFBck0sRUFDQSxJQUFBQSxFQUFBLEVBQW1CQSxFQUFBcU0sRUFBQW5JLE9BQW1CbEUsSUFBQSxDQUN0QyxNQUFBdU0sRUFBQUYsRUFBQXJNLEdBQ0F1TSxFQUFBQyxVQUFBQyxTQUFBLFlBQ0FGLEVBQUFDLFVBQUFFLE9BQUEsV0FFQU4sRUFBQWxKLE1BQ0F3QyxHQUFBNkcsRUFBQTdHLEdBQ0FuQixPQUFBZ0ksRUFBQWhJLE9BQ0FvSSxNQUFBSixFQUFBSSxRQUdBaEIsRUFBQXhCLFlBQUEsa0JBQUFpQyxLQUVDLElBQ0R0SSxPQUFBMEcsaUJBQUEsY0FDQWEsR0FBQSxFQUNBVyxNQUNDLEdBQ0RsSSxPQUFBMEcsaUJBQUEsVUFBQW9DLElBQ0EsR0FBQUEsRUFBQTVLLEtBQUFvSSxTQUFBbUIsRUFBQW5CLE9BR0EsT0FBQXdDLEVBQUE1SyxLQUFBdUgsTUFDQSxxQ0FDQStCLEVBQUF1QiwrQkFBQUQsRUFBQTVLLEtBQUFRLE1BQ0EsTUFDQSxpQkFDQTBKLEVBQUFVLEVBQUE1SyxLQUFBUSxLQUFBK0ksTUFHQyxHQUNEekosU0FBQTBJLGlCQUFBLFdBQUFvQyxJQUNBLElBQUFyQixFQUFBdUIsNEJBQ0EsT0FHQSxRQUFBQyxFQUFBSCxFQUFBSSxPQUFpQ0QsRUFBTUEsSUFBQUUsV0FDdkMsU0FBQUYsRUFBQUcsUUFDQSxPQUdBLE1BQUF0SixFQUFBZ0osRUFBQU8sTUFDQTNLLEVBQUFpSSxFQUFBdkYsaUNBQUF0QixHQUNBLGlCQUFBcEIsR0FBQVMsTUFBQVQsSUFDQW1KLEVBQUF4QixZQUFBLFlBQTJDM0gsS0FBQUksS0FBQVUsTUFBQWQsT0FHM0MsTUFBQTRLLEdBQUEsd0RBQ0F0TCxTQUFBMEksaUJBQUEsUUFBQW9DLElBQ0EsSUFBQUEsRUFDQSxPQUVBLElBQUFHLEVBQUFILEVBQUFJLE9BQ0EsS0FBQUQsR0FBQSxDQUNBLEdBQUFBLEVBQUFHLFNBQUEsTUFBQUgsRUFBQUcsU0FBQUgsRUFBQU0sS0FBQSxDQUNBLEdBQUFOLEVBQUE5SyxhQUFBLFFBQUFxTCxXQUFBLEtBQ0EsT0FHQSxHQUFBRixFQUFBRyxLQUFBQyxHQUFBVCxFQUFBTSxLQUFBQyxXQUFBRSxJQUNBLE9BRUEsTUFBQUMsRUFBQVYsRUFBQTlLLGFBQUEsY0FBQThLLEVBQUE5SyxhQUFBLFFBRUEsb0JBQUE2SCxLQUFBMkQsUUFNQSxHQUxBOUIsRUFBQXhCLFlBQUEsWUFBbURrRCxLQUFBSSxJQUNuRGIsRUFBQWMsc0JBQ0FkLEVBQUFlLG1CQUtBWixJQUFBRSxjQUVDLEdBQ0RuSixPQUFBMEcsaUJBQUEsU0FBQVksRUFBQSxLQUNBLEdBQUFDLEVBQ0FBLEdBQUEsTUFFQSxDQUNBLE1BQUE3SSxFQUFBaUksRUFBQXZGLGlDQUFBcEIsT0FBQUMsU0FDQSxpQkFBQXZCLEdBQUFTLE1BQUFULEtBQ0FtSixFQUFBeEIsWUFBQSxjQUFpRDNILFNBQ2pEaUosRUFBQWpKLE9BQ0F5SCxFQUFBeUIsU0FBQUQsTUFHQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMTEpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5mdW5jdGlvbiBjbGFtcChtaW4sIG1heCwgdmFsdWUpIHtcbiAgICByZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5mdW5jdGlvbiBjbGFtcExpbmUobGluZSkge1xuICAgIHJldHVybiBjbGFtcCgwLCBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcbiAgICBsZXQgZWxlbWVudHM7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgaWYgKCFlbGVtZW50cykge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudCwgbGluZSB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVsZW1lbnRzO1xuICAgIH07XG59KSgpO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmZ1bmN0aW9uIGdldEVsZW1lbnRzRm9yU291cmNlTGluZSh0YXJnZXRMaW5lKSB7XG4gICAgY29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgbGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG4gICAgICAgIGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGVudHJ5LmxpbmUgPiBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91cywgbmV4dDogZW50cnkgfTtcbiAgICAgICAgfVxuICAgICAgICBwcmV2aW91cyA9IGVudHJ5O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91cyB9O1xufVxuZXhwb3J0cy5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmU7XG4vKipcbiAqIEZpbmQgdGhlIGh0bWwgZWxlbWVudHMgdGhhdCBhcmUgYXQgYSBzcGVjaWZpYyBwaXhlbCBvZmZzZXQgb24gdGhlIHBhZ2UuXG4gKi9cbmZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpIHtcbiAgICBjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcbiAgICBjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuICAgIGxldCBsbyA9IC0xO1xuICAgIGxldCBoaSA9IGxpbmVzLmxlbmd0aCAtIDE7XG4gICAgd2hpbGUgKGxvICsgMSA8IGhpKSB7XG4gICAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKGxvICsgaGkpIC8gMik7XG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG4gICAgICAgICAgICBoaSA9IG1pZDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxvID0gbWlkO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGhpRWxlbWVudCA9IGxpbmVzW2hpXTtcbiAgICBjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0O1xuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5mdW5jdGlvbiBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSkge1xuICAgIGlmICghc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGxpbmUgPD0gMCkge1xuICAgICAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCAwKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgaWYgKCFwcmV2aW91cykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBzY3JvbGxUbyA9IDA7XG4gICAgY29uc3QgcmVjdCA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcbiAgICBpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcbiAgICAgICAgLy8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cbiAgICAgICAgY29uc3QgYmV0d2VlblByb2dyZXNzID0gKGxpbmUgLSBwcmV2aW91cy5saW5lKSAvIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcbiAgICAgICAgY29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcbiAgICAgICAgc2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgKHJlY3QuaGVpZ2h0ICogcHJvZ3Jlc3NJbkVsZW1lbnQpO1xuICAgIH1cbiAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5leHBvcnRzLnNjcm9sbFRvUmV2ZWFsU291cmNlTGluZSA9IHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZTtcbmZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCkge1xuICAgIGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuICAgIGlmIChwcmV2aW91cykge1xuICAgICAgICBjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcbiAgICAgICAgICAgIGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcbiAgICAgICAgICAgIHJldHVybiBjbGFtcExpbmUobGluZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG59XG5leHBvcnRzLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0ID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQ7XG4vKipcbiAqIFRyeSB0byBmaW5kIHRoZSBodG1sIGVsZW1lbnQgYnkgdXNpbmcgYSBmcmFnbWVudCBpZFxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KGZyYWdtZW50KSB7XG4gICAgcmV0dXJuIGdldENvZGVMaW5lRWxlbWVudHMoKS5maW5kKChlbGVtZW50KSA9PiB7XG4gICAgICAgIHJldHVybiBlbGVtZW50LmVsZW1lbnQuaWQgPT09IGZyYWdtZW50O1xuICAgIH0pO1xufVxuZXhwb3J0cy5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50ID0gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudDtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmV4cG9ydHMuY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZSkgPT4ge1xuICAgIHJldHVybiBuZXcgY2xhc3Mge1xuICAgICAgICBwb3N0TWVzc2FnZSh0eXBlLCBib2R5KSB7XG4gICAgICAgICAgICB2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICAgICAgc291cmNlOiBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc291cmNlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmNvbnN0IHBhc3NUaHJvdWdoTGlua1NjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOicsICdtYWlsdG86JywgJ3ZzY29kZTonLCAndnNjb2RlLWluc2lkZXJzJ107XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IG5vZGUgPSBldmVudC50YXJnZXQ7XG4gICAgd2hpbGUgKG5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSAmJiBub2RlLnRhZ05hbWUgPT09ICdBJyAmJiBub2RlLmhyZWYpIHtcbiAgICAgICAgICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpLnN0YXJ0c1dpdGgoJyMnKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFBhc3MgdGhyb3VnaCBrbm93biBzY2hlbWVzXG4gICAgICAgICAgICBpZiAocGFzc1Rocm91Z2hMaW5rU2NoZW1lcy5zb21lKHNjaGVtZSA9PiBub2RlLmhyZWYuc3RhcnRzV2l0aChzY2hlbWUpKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGhyZWZUZXh0ID0gbm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtaHJlZicpIHx8IG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJyk7XG4gICAgICAgICAgICAvLyBJZiBvcmlnaW5hbCBsaW5rIGRvZXNuJ3QgbG9vayBsaWtlIGEgdXJsLCBkZWxlZ2F0ZSBiYWNrIHRvIFZTIENvZGUgdG8gcmVzb2x2ZVxuICAgICAgICAgICAgaWYgKCEvXlthLXpcXC1dKzovaS50ZXN0KGhyZWZUZXh0KSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnb3BlbkxpbmsnLCB7IGhyZWY6IGhyZWZUZXh0IH0pO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG4gICAgfVxufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuICAgIGlmIChzY3JvbGxEaXNhYmxlZCkge1xuICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IGZhbHNlO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgY29uc3QgbGluZSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQod2luZG93LnNjcm9sbFkpO1xuICAgICAgICBpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdyZXZlYWxMaW5lJywgeyBsaW5lIH0pO1xuICAgICAgICAgICAgc3RhdGUubGluZSA9IGxpbmU7XG4gICAgICAgICAgICB2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuICAgICAgICB9XG4gICAgfVxufSwgNTApKTtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var i=t[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=11)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function i(e){const t=document.getElementById("vscode-markdown-preview-data");if(t){const n=t.getAttribute(e);if(n)return JSON.parse(n)}throw new Error(`Could not load data for ${e}`)}t.getData=i,t.getSettings=function(){if(o)return o;if(o=i("data-settings"))return o;throw new Error("Could not load settings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);function i(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const r=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName("code-line")){const n=+t.getAttribute("data-line");isNaN(n)||e.push({element:t,line:n})}}return e}})();function s(e){const t=Math.floor(e),n=r();let o=n[0]||null;for(const e of n){if(e.line===t)return{previous:e,next:void 0};if(e.line>t)return{previous:o,next:e};o=e}return{previous:o}}function c(e){const t=r(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1=n?i=e:o=e}const s=t[i],c=s.element.getBoundingClientRect();if(i>=1&&c.top>n){return{previous:t[o],next:s}}return{previous:s}}t.getElementsForSourceLine=s,t.getLineElementsAtPageOffset=c,t.scrollToRevealSourceLine=function(e){if(!o.getSettings().scrollPreviewWithEditor)return;if(e<=0)return void window.scroll(window.scrollX,0);const{previous:t,next:n}=s(e);if(!t)return;let i=0;const r=t.element.getBoundingClientRect(),c=r.top;if(n&&n.line!==t.line)i=c+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-c);else{const t=e-Math.floor(e);i=c+r.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+i))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=c(e);if(t){const o=t.element.getBoundingClientRect(),r=e-window.scrollY-o.top;if(n){const e=r/(n.element.getBoundingClientRect().top-o.top);return i(t.line+e*(n.line-t.line))}{const e=r/o.height;return i(t.line+e)}}return null},t.getLineElementForFragment=function(e){return r().find(t=>t.element.id===e)}},,,,,function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){(function(t){var n="Expected a function",o=NaN,i="[object Symbol]",r=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,d="object"==typeof self&&self&&self.Object===Object&&self,f=l||d||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return f.Date.now()};function h(e,t,o){var i,r,s,c,a,u,l=0,d=!1,f=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=i,o=r;return i=r=void 0,l=t,c=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||f&&e-l>=s}function E(){var e=v();if(y(e))return L(e);a=setTimeout(E,function(e){var n=t-(e-u);return f?m(n,s-(e-l)):n}(e))}function L(e){return a=void 0,g&&i?h(e):(i=r=void 0,c)}function M(){var e=v(),n=y(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(E,t),d?h(e):c}(u);if(f)return a=setTimeout(E,t),h(u)}return void 0===a&&(a=setTimeout(E,t)),c}return t=b(t)||0,w(o)&&(d=!!o.leading,s=(f="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),M.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},M.flush=function(){return void 0===a?c:L(v())},M}function w(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function b(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&g.call(e)==i}(e))return o;if(w(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=w(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(r,"");var n=c.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),h(e,t,{leading:i,maxWait:t,trailing:r})}}).call(this,n(6))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);t.createPosterForVsCode=(e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1);t.ActiveLineMarker=class{onDidChangeTextEditorSelection(e){const{previous:t}=o.getElementsForSourceLine(e);this._update(t&&t.element)}_update(e){this._unmarkActiveElement(this._current),this._markActiveElement(e),this._current=e}_unmarkActiveElement(e){e&&(e.className=e.className.replace(/\bcode-active-line\b/g,""))}_markActiveElement(e){e&&(e.className+=" code-active-line")}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(10),i=n(9),r=n(8),s=n(1),c=n(0),a=n(7);let u=!0;const l=new o.ActiveLineMarker,d=c.getSettings(),f=acquireVsCodeApi();let g=c.getData("data-state");f.setState(g);const p=r.createPosterForVsCode(f);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=(()=>{v()}),i.onceDocumentLoaded(()=>{d.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+d.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=a(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.line=t,e(t))}})();let v=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n{u=!0,v()},!0),window.addEventListener("message",e=>{if(e.data.source===d.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,d)}},!1),document.addEventListener("dblclick",e=>{if(!d.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=s.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||p.postMessage("didClick",{line:Math.floor(n)})});const h=["http:","https:","mailto:","vscode:","vscode-insiders:"];document.addEventListener("click",e=>{if(!e)return;let t=e.target;for(;t;){if(t.tagName&&"A"===t.tagName&&t.href){if(t.getAttribute("href").startsWith("#"))return;if(h.some(e=>t.href.startsWith(e)))return;const n=t.getAttribute("data-href")||t.getAttribute("href");return/^[a-z\-]+:/i.test(n)?void 0:(p.postMessage("openLink",{href:n}),e.preventDefault(),void e.stopPropagation())}t=t.parentNode}},!0),window.addEventListener("scroll",a(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,f.setState(g))}},50))}]); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9kYXNoLnRocm90dGxlL2luZGV4LmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9pbmRleC50cyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiY29uZmlndXJhYmxlIiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJ2YWx1ZSIsIm4iLCJfX2VzTW9kdWxlIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY2FjaGVkU2V0dGluZ3MiLCJ1bmRlZmluZWQiLCJnZXREYXRhIiwia2V5IiwiZWxlbWVudCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJkYXRhIiwiZ2V0QXR0cmlidXRlIiwiSlNPTiIsInBhcnNlIiwiRXJyb3IiLCJnZXRTZXR0aW5ncyIsInNldHRpbmdzXzEiLCJjbGFtcExpbmUiLCJsaW5lIiwibWluIiwibWF4IiwibGluZUNvdW50IiwiTWF0aCIsImdldENvZGVMaW5lRWxlbWVudHMiLCJlbGVtZW50cyIsImJvZHkiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiaXNOYU4iLCJwdXNoIiwiZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lIiwidGFyZ2V0TGluZSIsImxpbmVOdW1iZXIiLCJmbG9vciIsImxpbmVzIiwicHJldmlvdXMiLCJlbnRyeSIsIm5leHQiLCJnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQiLCJvZmZzZXQiLCJwb3NpdGlvbiIsIndpbmRvdyIsInNjcm9sbFkiLCJsbyIsImhpIiwibGVuZ3RoIiwibWlkIiwiYm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiZyIsInRoaXMiLCJGdW5jdGlvbiIsImV2YWwiLCJlIiwiZ2xvYmFsIiwiRlVOQ19FUlJPUl9URVhUIiwiTkFOIiwic3ltYm9sVGFnIiwicmVUcmltIiwicmVJc0JhZEhleCIsInJlSXNCaW5hcnkiLCJyZUlzT2N0YWwiLCJmcmVlUGFyc2VJbnQiLCJwYXJzZUludCIsImZyZWVHbG9iYWwiLCJmcmVlU2VsZiIsInNlbGYiLCJyb290Iiwib2JqZWN0VG9TdHJpbmciLCJ0b1N0cmluZyIsIm5hdGl2ZU1heCIsIm5hdGl2ZU1pbiIsIm5vdyIsIkRhdGUiLCJkZWJvdW5jZSIsImZ1bmMiLCJ3YWl0Iiwib3B0aW9ucyIsImxhc3RBcmdzIiwibGFzdFRoaXMiLCJtYXhXYWl0IiwicmVzdWx0IiwidGltZXJJZCIsImxhc3RDYWxsVGltZSIsImxhc3RJbnZva2VUaW1lIiwibGVhZGluZyIsIm1heGluZyIsInRyYWlsaW5nIiwiVHlwZUVycm9yIiwiaW52b2tlRnVuYyIsInRpbWUiLCJhcmdzIiwidGhpc0FyZyIsImFwcGx5Iiwic2hvdWxkSW52b2tlIiwidGltZVNpbmNlTGFzdENhbGwiLCJ0aW1lckV4cGlyZWQiLCJ0cmFpbGluZ0VkZ2UiLCJzZXRUaW1lb3V0IiwicmVtYWluaW5nV2FpdCIsImRlYm91bmNlZCIsImlzSW52b2tpbmciLCJhcmd1bWVudHMiLCJsZWFkaW5nRWRnZSIsInRvTnVtYmVyIiwiaXNPYmplY3QiLCJjYW5jZWwiLCJjbGVhclRpbWVvdXQiLCJmbHVzaCIsInR5cGUiLCJpc09iamVjdExpa2UiLCJpc1N5bWJvbCIsIm90aGVyIiwidmFsdWVPZiIsInJlcGxhY2UiLCJpc0JpbmFyeSIsInRlc3QiLCJzbGljZSIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsInZzY29kZSIsIltvYmplY3QgT2JqZWN0XSIsInBvc3RNZXNzYWdlIiwic291cmNlIiwib25jZURvY3VtZW50TG9hZGVkIiwiZiIsInJlYWR5U3RhdGUiLCJhZGRFdmVudExpc3RlbmVyIiwic2Nyb2xsX3N5bmNfMSIsIkFjdGl2ZUxpbmVNYXJrZXIiLCJfdXBkYXRlIiwiYmVmb3JlIiwiX3VubWFya0FjdGl2ZUVsZW1lbnQiLCJfY3VycmVudCIsIl9tYXJrQWN0aXZlRWxlbWVudCIsImNsYXNzTmFtZSIsImFjdGl2ZUxpbmVNYXJrZXJfMSIsImV2ZW50c18xIiwibWVzc2FnaW5nXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwic2V0dGluZ3MiLCJhY3F1aXJlVnNDb2RlQXBpIiwic3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsImluaXRpYWxMaW5lIiwib25VcGRhdGVWaWV3IiwiZG9TY3JvbGwiLCJpbWFnZUluZm8iLCJpbWFnZXMiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImltZyIsImNsYXNzTGlzdCIsImNvbnRhaW5zIiwicmVtb3ZlIiwid2lkdGgiLCJldmVudCIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwidGFnTmFtZSIsInBhZ2VZIiwicGFzc1Rocm91Z2hMaW5rU2NoZW1lcyIsImhyZWYiLCJzdGFydHNXaXRoIiwic29tZSIsInNjaGVtZSIsImhyZWZUZXh0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iXSwibWFwcGluZ3MiOiJhQUNBLElBQUFBLEtBR0EsU0FBQUMsRUFBQUMsR0FHQSxHQUFBRixFQUFBRSxHQUNBLE9BQUFGLEVBQUFFLEdBQUFDLFFBR0EsSUFBQUMsRUFBQUosRUFBQUUsSUFDQUcsRUFBQUgsRUFDQUksR0FBQSxFQUNBSCxZQVVBLE9BTkFJLEVBQUFMLEdBQUFNLEtBQUFKLEVBQUFELFFBQUFDLElBQUFELFFBQUFGLEdBR0FHLEVBQUFFLEdBQUEsRUFHQUYsRUFBQUQsUUFLQUYsRUFBQVEsRUFBQUYsRUFHQU4sRUFBQVMsRUFBQVYsRUFHQUMsRUFBQVUsRUFBQSxTQUFBUixFQUFBUyxFQUFBQyxHQUNBWixFQUFBYSxFQUFBWCxFQUFBUyxJQUNBRyxPQUFBQyxlQUFBYixFQUFBUyxHQUNBSyxjQUFBLEVBQ0FDLFlBQUEsRUFDQUMsSUFBQU4sS0FNQVosRUFBQW1CLEVBQUEsU0FBQWpCLEdBQ0FZLE9BQUFDLGVBQUFiLEVBQUEsY0FBaURrQixPQUFBLEtBSWpEcEIsRUFBQXFCLEVBQUEsU0FBQWxCLEdBQ0EsSUFBQVMsRUFBQVQsS0FBQW1CLFdBQ0EsV0FBMkIsT0FBQW5CLEVBQUEsU0FDM0IsV0FBaUMsT0FBQUEsR0FFakMsT0FEQUgsRUFBQVUsRUFBQUUsRUFBQSxJQUFBQSxHQUNBQSxHQUlBWixFQUFBYSxFQUFBLFNBQUFVLEVBQUFDLEdBQXNELE9BQUFWLE9BQUFXLFVBQUFDLGVBQUFuQixLQUFBZ0IsRUFBQUMsSUFHdER4QixFQUFBMkIsRUFBQSxHQUlBM0IsSUFBQTRCLEVBQUEsbUNDOURBZCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxJQUFBUyxPQUFBQyxFQUNBLFNBQUFDLEVBQUFDLEdBQ0EsTUFBQUMsRUFBQUMsU0FBQUMsZUFBQSxnQ0FDQSxHQUFBRixFQUFBLENBQ0EsTUFBQUcsRUFBQUgsRUFBQUksYUFBQUwsR0FDQSxHQUFBSSxFQUNBLE9BQUFFLEtBQUFDLE1BQUFILEdBR0EsVUFBQUksaUNBQStDUixLQUUvQzlCLEVBQUE2QixVQVdBN0IsRUFBQXVDLFlBVkEsV0FDQSxHQUFBWixFQUNBLE9BQUFBLEVBR0EsR0FEQUEsRUFBQUUsRUFBQSxpQkFFQSxPQUFBRixFQUVBLFVBQUFXLE1BQUEsMERDckJBMUIsT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFDOUMsTUFBQXNCLEVBQUExQyxFQUFBLEdBSUEsU0FBQTJDLEVBQUFDLEdBQ0EsT0FKQUMsRUFJQSxFQUpBQyxFQUlBSixFQUFBRCxjQUFBTSxVQUFBLEVBSkEzQixFQUlBd0IsRUFIQUksS0FBQUgsSUFBQUMsRUFBQUUsS0FBQUYsSUFBQUQsRUFBQXpCLElBREEsSUFBQXlCLEVBQUFDLEVBQUExQixFQU1BLE1BQUE2QixFQUFBLE1BQ0EsSUFBQUMsRUFDQSxXQUNBLElBQUFBLEVBQUEsQ0FDQUEsSUFBeUJqQixRQUFBQyxTQUFBaUIsS0FBQVAsS0FBQSxJQUN6QixVQUFBWCxLQUFBQyxTQUFBa0IsdUJBQUEsY0FDQSxNQUFBUixHQUFBWCxFQUFBSSxhQUFBLGFBQ0FnQixNQUFBVCxJQUNBTSxFQUFBSSxNQUFtQ3JCLFVBQUFXLFVBSW5DLE9BQUFNLElBWkEsR0FxQkEsU0FBQUssRUFBQUMsR0FDQSxNQUFBQyxFQUFBVCxLQUFBVSxNQUFBRixHQUNBRyxFQUFBVixJQUNBLElBQUFXLEVBQUFELEVBQUEsU0FDQSxVQUFBRSxLQUFBRixFQUFBLENBQ0EsR0FBQUUsRUFBQWpCLE9BQUFhLEVBQ0EsT0FBb0JHLFNBQUFDLEVBQUFDLFVBQUFoQyxHQUVwQixHQUFBK0IsRUFBQWpCLEtBQUFhLEVBQ0EsT0FBb0JHLFdBQUFFLEtBQUFELEdBRXBCRCxFQUFBQyxFQUVBLE9BQVlELFlBTVosU0FBQUcsRUFBQUMsR0FDQSxNQUFBTCxFQUFBVixJQUNBZ0IsRUFBQUQsRUFBQUUsT0FBQUMsUUFDQSxJQUFBQyxHQUFBLEVBQ0FDLEVBQUFWLEVBQUFXLE9BQUEsRUFDQSxLQUFBRixFQUFBLEVBQUFDLEdBQUEsQ0FDQSxNQUFBRSxFQUFBdkIsS0FBQVUsT0FBQVUsRUFBQUMsR0FBQSxHQUNBRyxFQUFBYixFQUFBWSxHQUFBdEMsUUFBQXdDLHdCQUNBRCxFQUFBRSxJQUFBRixFQUFBRyxRQUFBVixFQUNBSSxFQUFBRSxFQUdBSCxFQUFBRyxFQUdBLE1BQUFLLEVBQUFqQixFQUFBVSxHQUNBUSxFQUFBRCxFQUFBM0MsUUFBQXdDLHdCQUNBLEdBQUFKLEdBQUEsR0FBQVEsRUFBQUgsSUFBQVQsRUFBQSxDQUVBLE9BQWdCTCxTQURoQkQsRUFBQVMsR0FDZ0JOLEtBQUFjLEdBRWhCLE9BQVloQixTQUFBZ0IsR0F6QloxRSxFQUFBcUQsMkJBMkJBckQsRUFBQTZELDhCQStCQTdELEVBQUE0RSx5QkEzQkEsU0FBQWxDLEdBQ0EsSUFBQUYsRUFBQUQsY0FBQXNDLHdCQUNBLE9BRUEsR0FBQW5DLEdBQUEsRUFFQSxZQURBc0IsT0FBQWMsT0FBQWQsT0FBQWUsUUFBQSxHQUdBLE1BQUFyQixTQUFXQSxFQUFBRSxRQUFpQlAsRUFBQVgsR0FDNUIsSUFBQWdCLEVBQ0EsT0FFQSxJQUFBc0IsRUFBQSxFQUNBLE1BQUFDLEVBQUF2QixFQUFBM0IsUUFBQXdDLHdCQUNBVyxFQUFBRCxFQUFBVCxJQUNBLEdBQUFaLEtBQUFsQixPQUFBZ0IsRUFBQWhCLEtBSUFzQyxFQUFBRSxHQUZBeEMsRUFBQWdCLEVBQUFoQixPQUFBa0IsRUFBQWxCLEtBQUFnQixFQUFBaEIsT0FDQWtCLEVBQUE3QixRQUFBd0Msd0JBQUFDLElBQUFVLE9BR0EsQ0FDQSxNQUFBQyxFQUFBekMsRUFBQUksS0FBQVUsTUFBQWQsR0FDQXNDLEVBQUFFLEVBQUFELEVBQUFSLE9BQUFVLEVBRUFuQixPQUFBYyxPQUFBZCxPQUFBZSxRQUFBakMsS0FBQUYsSUFBQSxFQUFBb0IsT0FBQUMsUUFBQWUsS0FxQkFoRixFQUFBb0YsaUNBbEJBLFNBQUF0QixHQUNBLE1BQUFKLFNBQVdBLEVBQUFFLFFBQWlCQyxFQUFBQyxHQUM1QixHQUFBSixFQUFBLENBQ0EsTUFBQTJCLEVBQUEzQixFQUFBM0IsUUFBQXdDLHdCQUNBZSxFQUFBeEIsRUFBQUUsT0FBQUMsUUFBQW9CLEVBQUFiLElBQ0EsR0FBQVosRUFBQSxDQUNBLE1BQUEyQixFQUFBRCxHQUFBMUIsRUFBQTdCLFFBQUF3Qyx3QkFBQUMsSUFBQWEsRUFBQWIsS0FFQSxPQUFBL0IsRUFEQWlCLEVBQUFoQixLQUFBNkMsR0FBQTNCLEVBQUFsQixLQUFBZ0IsRUFBQWhCLE9BR0EsQ0FDQSxNQUFBOEMsRUFBQUYsRUFBQUQsRUFBQSxPQUVBLE9BQUE1QyxFQURBaUIsRUFBQWhCLEtBQUE4QyxJQUlBLGFBV0F4RixFQUFBeUYsMEJBTEEsU0FBQUMsR0FDQSxPQUFBM0MsSUFBQTRDLEtBQUE1RCxHQUNBQSxVQUFBNkQsS0FBQUYsdUJDcElBLElBQUFHLEVBR0FBLEVBQUEsV0FDQSxPQUFBQyxLQURBLEdBSUEsSUFFQUQsS0FBQUUsU0FBQSxjQUFBQSxLQUFBLEVBQUFDLE1BQUEsUUFDQyxNQUFBQyxHQUVELGlCQUFBakMsU0FBQTZCLEVBQUE3QixRQU9BL0QsRUFBQUQsUUFBQTZGLG9CQ25CQSxTQUFBSyxHQVVBLElBQUFDLEVBQUEsc0JBR0FDLEVBQUEsSUFHQUMsRUFBQSxrQkFHQUMsRUFBQSxhQUdBQyxFQUFBLHFCQUdBQyxFQUFBLGFBR0FDLEVBQUEsY0FHQUMsRUFBQUMsU0FHQUMsRUFBQSxpQkFBQVYsUUFBQXRGLGlCQUFBc0YsRUFHQVcsRUFBQSxpQkFBQUMsaUJBQUFsRyxpQkFBQWtHLEtBR0FDLEVBQUFILEdBQUFDLEdBQUFkLFNBQUEsY0FBQUEsR0FVQWlCLEVBUEFwRyxPQUFBVyxVQU9BMEYsU0FHQUMsRUFBQXBFLEtBQUFGLElBQ0F1RSxFQUFBckUsS0FBQUgsSUFrQkF5RSxFQUFBLFdBQ0EsT0FBQUwsRUFBQU0sS0FBQUQsT0F5REEsU0FBQUUsRUFBQUMsRUFBQUMsRUFBQUMsR0FDQSxJQUFBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUNBQyxFQUFBLEVBQ0FDLEdBQUEsRUFDQUMsR0FBQSxFQUNBQyxHQUFBLEVBRUEsc0JBQUFaLEVBQ0EsVUFBQWEsVUFBQWpDLEdBVUEsU0FBQWtDLEVBQUFDLEdBQ0EsSUFBQUMsRUFBQWIsRUFDQWMsRUFBQWIsRUFLQSxPQUhBRCxFQUFBQyxPQUFBL0YsRUFDQW9HLEVBQUFNLEVBQ0FULEVBQUFOLEVBQUFrQixNQUFBRCxFQUFBRCxHQXFCQSxTQUFBRyxFQUFBSixHQUNBLElBQUFLLEVBQUFMLEVBQUFQLEVBTUEsWUFBQW5HLElBQUFtRyxHQUFBWSxHQUFBbkIsR0FDQW1CLEVBQUEsR0FBQVQsR0FOQUksRUFBQU4sR0FNQUosRUFHQSxTQUFBZ0IsSUFDQSxJQUFBTixFQUFBbEIsSUFDQSxHQUFBc0IsRUFBQUosR0FDQSxPQUFBTyxFQUFBUCxHQUdBUixFQUFBZ0IsV0FBQUYsRUF6QkEsU0FBQU4sR0FDQSxJQUVBVCxFQUFBTCxHQUZBYyxFQUFBUCxHQUlBLE9BQUFHLEVBQUFmLEVBQUFVLEVBQUFELEdBSEFVLEVBQUFOLElBR0FILEVBb0JBa0IsQ0FBQVQsSUFHQSxTQUFBTyxFQUFBUCxHQUtBLE9BSkFSLE9BQUFsRyxFQUlBdUcsR0FBQVQsRUFDQVcsRUFBQUMsSUFFQVosRUFBQUMsT0FBQS9GLEVBQ0FpRyxHQWVBLFNBQUFtQixJQUNBLElBQUFWLEVBQUFsQixJQUNBNkIsRUFBQVAsRUFBQUosR0FNQSxHQUpBWixFQUFBd0IsVUFDQXZCLEVBQUE3QixLQUNBaUMsRUFBQU8sRUFFQVcsRUFBQSxDQUNBLFFBQUFySCxJQUFBa0csRUFDQSxPQXZFQSxTQUFBUSxHQU1BLE9BSkFOLEVBQUFNLEVBRUFSLEVBQUFnQixXQUFBRixFQUFBcEIsR0FFQVMsRUFBQUksRUFBQUMsR0FBQVQsRUFpRUFzQixDQUFBcEIsR0FFQSxHQUFBRyxFQUdBLE9BREFKLEVBQUFnQixXQUFBRixFQUFBcEIsR0FDQWEsRUFBQU4sR0FNQSxZQUhBbkcsSUFBQWtHLElBQ0FBLEVBQUFnQixXQUFBRixFQUFBcEIsSUFFQUssRUFJQSxPQXhHQUwsRUFBQTRCLEVBQUE1QixJQUFBLEVBQ0E2QixFQUFBNUIsS0FDQVEsSUFBQVIsRUFBQVEsUUFFQUwsR0FEQU0sRUFBQSxZQUFBVCxHQUNBUCxFQUFBa0MsRUFBQTNCLEVBQUFHLFVBQUEsRUFBQUosR0FBQUksRUFDQU8sRUFBQSxhQUFBVixNQUFBVSxZQWlHQWEsRUFBQU0sT0FuQ0EsZ0JBQ0ExSCxJQUFBa0csR0FDQXlCLGFBQUF6QixHQUVBRSxFQUFBLEVBQ0FOLEVBQUFLLEVBQUFKLEVBQUFHLE9BQUFsRyxHQStCQW9ILEVBQUFRLE1BNUJBLFdBQ0EsWUFBQTVILElBQUFrRyxFQUFBRCxFQUFBZ0IsRUFBQXpCLE1BNEJBNEIsRUEwRkEsU0FBQUssRUFBQW5JLEdBQ0EsSUFBQXVJLFNBQUF2SSxFQUNBLFFBQUFBLElBQUEsVUFBQXVJLEdBQUEsWUFBQUEsR0E0RUEsU0FBQUwsRUFBQWxJLEdBQ0Esb0JBQUFBLEVBQ0EsT0FBQUEsRUFFQSxHQWhDQSxTQUFBQSxHQUNBLHVCQUFBQSxHQXRCQSxTQUFBQSxHQUNBLFFBQUFBLEdBQUEsaUJBQUFBLEVBc0JBd0ksQ0FBQXhJLElBQUE4RixFQUFBM0csS0FBQWEsSUFBQW1GLEVBOEJBc0QsQ0FBQXpJLEdBQ0EsT0FBQWtGLEVBRUEsR0FBQWlELEVBQUFuSSxHQUFBLENBQ0EsSUFBQTBJLEVBQUEsbUJBQUExSSxFQUFBMkksUUFBQTNJLEVBQUEySSxVQUFBM0ksRUFDQUEsRUFBQW1JLEVBQUFPLEtBQUEsR0FBQUEsRUFFQSxvQkFBQTFJLEVBQ0EsV0FBQUEsT0FFQUEsSUFBQTRJLFFBQUF4RCxFQUFBLElBQ0EsSUFBQXlELEVBQUF2RCxFQUFBd0QsS0FBQTlJLEdBQ0EsT0FBQTZJLEdBQUF0RCxFQUFBdUQsS0FBQTlJLEdBQ0F3RixFQUFBeEYsRUFBQStJLE1BQUEsR0FBQUYsRUFBQSxLQUNBeEQsRUFBQXlELEtBQUE5SSxHQUFBa0YsR0FBQWxGLEVBR0FqQixFQUFBRCxRQTlJQSxTQUFBdUgsRUFBQUMsRUFBQUMsR0FDQSxJQUFBUSxHQUFBLEVBQ0FFLEdBQUEsRUFFQSxzQkFBQVosRUFDQSxVQUFBYSxVQUFBakMsR0FNQSxPQUpBa0QsRUFBQTVCLEtBQ0FRLEVBQUEsWUFBQVIsTUFBQVEsVUFDQUUsRUFBQSxhQUFBVixNQUFBVSxZQUVBYixFQUFBQyxFQUFBQyxHQUNBUyxVQUNBTCxRQUFBSixFQUNBVyw4RENqVEF2SCxPQUFBQyxlQUFBYixFQUFBLGNBQThDa0IsT0FBQSxJQUM5QyxNQUFBc0IsRUFBQTFDLEVBQUEsR0FDQUUsRUFBQWtLLHNCQUFBLENBQUFDLEdBQ0EsVUFDQUMsWUFBQVgsRUFBQXhHLEdBQ0FrSCxFQUFBRSxhQUNBWixPQUNBYSxPQUFBOUgsRUFBQUQsY0FBQStILE9BQ0FySCwwQ0NSQXJDLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBUzlDbEIsRUFBQXVLLG1CQVJBLFNBQUFDLEdBQ0EsWUFBQXhJLFNBQUF5SSxZQUFBLGtCQUFBekksU0FBQXlJLFdBQ0F6SSxTQUFBMEksaUJBQUEsbUJBQUFGLEdBR0FBLG1DQ1ZBNUosT0FBQUMsZUFBQWIsRUFBQSxjQUE4Q2tCLE9BQUEsSUFLOUMsTUFBQXlKLEVBQUE3SyxFQUFBLEdBd0JBRSxFQUFBNEssdUJBdEJBUiwrQkFBQTFILEdBQ0EsTUFBQWdCLFNBQWVBLEdBQVdpSCxFQUFBdEgseUJBQUFYLEdBQzFCb0QsS0FBQStFLFFBQUFuSCxLQUFBM0IsU0FFQXFJLFFBQUFVLEdBQ0FoRixLQUFBaUYscUJBQUFqRixLQUFBa0YsVUFDQWxGLEtBQUFtRixtQkFBQUgsR0FDQWhGLEtBQUFrRixTQUFBRixFQUVBVixxQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixVQUFBbkosRUFBQW1KLFVBQUFwQixRQUFBLDZCQUVBTSxtQkFBQXJJLEdBQ0FBLElBR0FBLEVBQUFtSixXQUFBLHFEQ3RCQXRLLE9BQUFDLGVBQUFiLEVBQUEsY0FBOENrQixPQUFBLElBQzlDLE1BQUFpSyxFQUFBckwsRUFBQSxJQUNBc0wsRUFBQXRMLEVBQUEsR0FDQXVMLEVBQUF2TCxFQUFBLEdBQ0E2SyxFQUFBN0ssRUFBQSxHQUNBMEMsRUFBQTFDLEVBQUEsR0FDQXdMLEVBQUF4TCxFQUFBLEdBQ0EsSUFBQXlMLEdBQUEsRUFDQSxNQUFBQyxFQUFBLElBQUFMLEVBQUFQLGlCQUNBYSxFQUFBakosRUFBQUQsY0FDQTRILEVBQUF1QixtQkFFQSxJQUFBQyxFQUFBbkosRUFBQVgsUUFBQSxjQUNBc0ksRUFBQXlCLFNBQUFELEdBQ0EsTUFBQUUsRUFBQVIsRUFBQW5CLHNCQUFBQyxHQUNBbkcsT0FBQThILFdBQUFDLFVBQUFGLEdBQ0E3SCxPQUFBZ0ksb0JBQUFELFVBQUFGLEdBQ0E3SCxPQUFBaUksT0FBQSxNQUNBQyxNQUVBZCxFQUFBYixtQkFBQSxLQUNBa0IsRUFBQTVHLHlCQUNBaUUsV0FBQSxLQUVBLEdBQUE2QyxFQUFBakcsU0FBQSxDQUNBLE1BQUEzRCxFQUFBNEksRUFBQWxGLDBCQUFBa0csRUFBQWpHLFVBQ0EzRCxJQUNBd0osR0FBQSxFQUNBWixFQUFBL0YseUJBQUE3QyxFQUFBVyxXQUdBLENBQ0EsTUFBQXlKLEdBQUFWLEVBQUEvSSxLQUNBUyxNQUFBZ0osS0FDQVosR0FBQSxFQUNBWixFQUFBL0YseUJBQUF1SCxNQUdTLEtBR1QsTUFBQUMsRUFBQSxNQUNBLE1BQUFDLEVBQUFmLEVBQUE1SSxJQUNBNkksR0FBQSxFQUNBWixFQUFBL0YseUJBQUFsQyxJQUNLLElBQ0wsT0FBQUEsRUFBQStJLEtBQ0F0SSxNQUFBVCxLQUNBK0ksRUFBQS9JLE9BQ0EySixFQUFBM0osTUFSQSxHQVlBLElBQUF3SixFQUFBWixFQUFBLEtBQ0EsTUFBQWdCLEtBQ0EsSUFBQUMsRUFBQXZLLFNBQUF3SyxxQkFBQSxPQUNBLEdBQUFELEVBQUEsQ0FDQSxJQUFBck0sRUFDQSxJQUFBQSxFQUFBLEVBQW1CQSxFQUFBcU0sRUFBQW5JLE9BQW1CbEUsSUFBQSxDQUN0QyxNQUFBdU0sRUFBQUYsRUFBQXJNLEdBQ0F1TSxFQUFBQyxVQUFBQyxTQUFBLFlBQ0FGLEVBQUFDLFVBQUFFLE9BQUEsV0FFQU4sRUFBQWxKLE1BQ0F3QyxHQUFBNkcsRUFBQTdHLEdBQ0FuQixPQUFBZ0ksRUFBQWhJLE9BQ0FvSSxNQUFBSixFQUFBSSxRQUdBaEIsRUFBQXhCLFlBQUEsa0JBQUFpQyxLQUVDLElBQ0R0SSxPQUFBMEcsaUJBQUEsY0FDQWEsR0FBQSxFQUNBVyxNQUNDLEdBQ0RsSSxPQUFBMEcsaUJBQUEsVUFBQW9DLElBQ0EsR0FBQUEsRUFBQTVLLEtBQUFvSSxTQUFBbUIsRUFBQW5CLE9BR0EsT0FBQXdDLEVBQUE1SyxLQUFBdUgsTUFDQSxxQ0FDQStCLEVBQUF1QiwrQkFBQUQsRUFBQTVLLEtBQUFRLE1BQ0EsTUFDQSxpQkFDQTBKLEVBQUFVLEVBQUE1SyxLQUFBUSxLQUFBK0ksTUFHQyxHQUNEekosU0FBQTBJLGlCQUFBLFdBQUFvQyxJQUNBLElBQUFyQixFQUFBdUIsNEJBQ0EsT0FHQSxRQUFBQyxFQUFBSCxFQUFBSSxPQUFpQ0QsRUFBTUEsSUFBQUUsV0FDdkMsU0FBQUYsRUFBQUcsUUFDQSxPQUdBLE1BQUF0SixFQUFBZ0osRUFBQU8sTUFDQTNLLEVBQUFpSSxFQUFBdkYsaUNBQUF0QixHQUNBLGlCQUFBcEIsR0FBQVMsTUFBQVQsSUFDQW1KLEVBQUF4QixZQUFBLFlBQTJDM0gsS0FBQUksS0FBQVUsTUFBQWQsT0FHM0MsTUFBQTRLLEdBQUEseURBQ0F0TCxTQUFBMEksaUJBQUEsUUFBQW9DLElBQ0EsSUFBQUEsRUFDQSxPQUVBLElBQUFHLEVBQUFILEVBQUFJLE9BQ0EsS0FBQUQsR0FBQSxDQUNBLEdBQUFBLEVBQUFHLFNBQUEsTUFBQUgsRUFBQUcsU0FBQUgsRUFBQU0sS0FBQSxDQUNBLEdBQUFOLEVBQUE5SyxhQUFBLFFBQUFxTCxXQUFBLEtBQ0EsT0FHQSxHQUFBRixFQUFBRyxLQUFBQyxHQUFBVCxFQUFBTSxLQUFBQyxXQUFBRSxJQUNBLE9BRUEsTUFBQUMsRUFBQVYsRUFBQTlLLGFBQUEsY0FBQThLLEVBQUE5SyxhQUFBLFFBRUEsb0JBQUE2SCxLQUFBMkQsUUFNQSxHQUxBOUIsRUFBQXhCLFlBQUEsWUFBbURrRCxLQUFBSSxJQUNuRGIsRUFBQWMsc0JBQ0FkLEVBQUFlLG1CQUtBWixJQUFBRSxjQUVDLEdBQ0RuSixPQUFBMEcsaUJBQUEsU0FBQVksRUFBQSxLQUNBLEdBQUFDLEVBQ0FBLEdBQUEsTUFFQSxDQUNBLE1BQUE3SSxFQUFBaUksRUFBQXZGLGlDQUFBcEIsT0FBQUMsU0FDQSxpQkFBQXZCLEdBQUFTLE1BQUFULEtBQ0FtSixFQUFBeEIsWUFBQSxjQUFpRDNILFNBQ2pEaUosRUFBQWpKLE9BQ0F5SCxFQUFBeUIsU0FBQUQsTUFHQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMTEpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5mdW5jdGlvbiBjbGFtcChtaW4sIG1heCwgdmFsdWUpIHtcbiAgICByZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5mdW5jdGlvbiBjbGFtcExpbmUobGluZSkge1xuICAgIHJldHVybiBjbGFtcCgwLCBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcbiAgICBsZXQgZWxlbWVudHM7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgaWYgKCFlbGVtZW50cykge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudCwgbGluZSB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVsZW1lbnRzO1xuICAgIH07XG59KSgpO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmZ1bmN0aW9uIGdldEVsZW1lbnRzRm9yU291cmNlTGluZSh0YXJnZXRMaW5lKSB7XG4gICAgY29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgbGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG4gICAgICAgIGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGVudHJ5LmxpbmUgPiBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91cywgbmV4dDogZW50cnkgfTtcbiAgICAgICAgfVxuICAgICAgICBwcmV2aW91cyA9IGVudHJ5O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91cyB9O1xufVxuZXhwb3J0cy5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmU7XG4vKipcbiAqIEZpbmQgdGhlIGh0bWwgZWxlbWVudHMgdGhhdCBhcmUgYXQgYSBzcGVjaWZpYyBwaXhlbCBvZmZzZXQgb24gdGhlIHBhZ2UuXG4gKi9cbmZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpIHtcbiAgICBjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcbiAgICBjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuICAgIGxldCBsbyA9IC0xO1xuICAgIGxldCBoaSA9IGxpbmVzLmxlbmd0aCAtIDE7XG4gICAgd2hpbGUgKGxvICsgMSA8IGhpKSB7XG4gICAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKGxvICsgaGkpIC8gMik7XG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG4gICAgICAgICAgICBoaSA9IG1pZDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxvID0gbWlkO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGhpRWxlbWVudCA9IGxpbmVzW2hpXTtcbiAgICBjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0O1xuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5mdW5jdGlvbiBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSkge1xuICAgIGlmICghc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGxpbmUgPD0gMCkge1xuICAgICAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCAwKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgaWYgKCFwcmV2aW91cykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBzY3JvbGxUbyA9IDA7XG4gICAgY29uc3QgcmVjdCA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcbiAgICBpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcbiAgICAgICAgLy8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cbiAgICAgICAgY29uc3QgYmV0d2VlblByb2dyZXNzID0gKGxpbmUgLSBwcmV2aW91cy5saW5lKSAvIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcbiAgICAgICAgY29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcbiAgICAgICAgc2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgKHJlY3QuaGVpZ2h0ICogcHJvZ3Jlc3NJbkVsZW1lbnQpO1xuICAgIH1cbiAgICB3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5leHBvcnRzLnNjcm9sbFRvUmV2ZWFsU291cmNlTGluZSA9IHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZTtcbmZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCkge1xuICAgIGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuICAgIGlmIChwcmV2aW91cykge1xuICAgICAgICBjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcbiAgICAgICAgICAgIGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcbiAgICAgICAgICAgIHJldHVybiBjbGFtcExpbmUobGluZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG59XG5leHBvcnRzLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0ID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQ7XG4vKipcbiAqIFRyeSB0byBmaW5kIHRoZSBodG1sIGVsZW1lbnQgYnkgdXNpbmcgYSBmcmFnbWVudCBpZFxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KGZyYWdtZW50KSB7XG4gICAgcmV0dXJuIGdldENvZGVMaW5lRWxlbWVudHMoKS5maW5kKChlbGVtZW50KSA9PiB7XG4gICAgICAgIHJldHVybiBlbGVtZW50LmVsZW1lbnQuaWQgPT09IGZyYWdtZW50O1xuICAgIH0pO1xufVxuZXhwb3J0cy5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50ID0gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudDtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmV4cG9ydHMuY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZSkgPT4ge1xuICAgIHJldHVybiBuZXcgY2xhc3Mge1xuICAgICAgICBwb3N0TWVzc2FnZSh0eXBlLCBib2R5KSB7XG4gICAgICAgICAgICB2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICAgICAgc291cmNlOiBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc291cmNlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmNvbnN0IHBhc3NUaHJvdWdoTGlua1NjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOicsICdtYWlsdG86JywgJ3ZzY29kZTonLCAndnNjb2RlLWluc2lkZXJzOiddO1xuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG4gICAgaWYgKCFldmVudCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBub2RlID0gZXZlbnQudGFyZ2V0O1xuICAgIHdoaWxlIChub2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG4gICAgICAgICAgICBpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBQYXNzIHRocm91Z2gga25vd24gc2NoZW1lc1xuICAgICAgICAgICAgaWYgKHBhc3NUaHJvdWdoTGlua1NjaGVtZXMuc29tZShzY2hlbWUgPT4gbm9kZS5ocmVmLnN0YXJ0c1dpdGgoc2NoZW1lKSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBocmVmVGV4dCA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLWhyZWYnKSB8fCBub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpO1xuICAgICAgICAgICAgLy8gSWYgb3JpZ2luYWwgbGluayBkb2Vzbid0IGxvb2sgbGlrZSBhIHVybCwgZGVsZWdhdGUgYmFjayB0byBWUyBDb2RlIHRvIHJlc29sdmVcbiAgICAgICAgICAgIGlmICghL15bYS16XFwtXSs6L2kudGVzdChocmVmVGV4dCkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ29wZW5MaW5rJywgeyBocmVmOiBocmVmVGV4dCB9KTtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuICAgIH1cbn0sIHRydWUpO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcbiAgICBpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcbiAgICAgICAgc2Nyb2xsRGlzYWJsZWQgPSBmYWxzZTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KHdpbmRvdy5zY3JvbGxZKTtcbiAgICAgICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgncmV2ZWFsTGluZScsIHsgbGluZSB9KTtcbiAgICAgICAgICAgIHN0YXRlLmxpbmUgPSBsaW5lO1xuICAgICAgICAgICAgdnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbiAgICAgICAgfVxuICAgIH1cbn0sIDUwKSk7XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 1dbde3ed4e5..311c40b62d6 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -129,7 +129,7 @@ document.addEventListener('dblclick', event => { } }); -const passThroughLinkSchemes = ['http:', 'https:', 'mailto:', 'vscode:', 'vscode-insiders']; +const passThroughLinkSchemes = ['http:', 'https:', 'mailto:', 'vscode:', 'vscode-insiders:']; document.addEventListener('click', event => { if (!event) { diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 23364c4d69b..441055e5e71 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { OpenDocumentLinkCommand } from '../commands/openDocumentLink'; -import { getUriForLinkWithKnownExternalScheme } from '../util/links'; +import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/links'; const localize = nls.loadMessageBundle(); @@ -18,6 +18,10 @@ function parseLink( ): { uri: vscode.Uri, tooltip?: string } { const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); if (externalSchemeUri) { + // Normalize VS Code links to target currently running version + if (isOfScheme(Schemes.vscode, link) || isOfScheme(Schemes['vscode-insiders'], link)) { + return { uri: vscode.Uri.parse(link).with({ scheme: vscode.env.uriScheme }) }; + } return { uri: externalSchemeUri }; } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 9676278f3ab..d3b88f06c42 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -227,6 +227,11 @@ export class MarkdownEngine { const normalizeLink = md.normalizeLink; md.normalizeLink = (link: string) => { try { + // Normalize VS Code schemes to target the current version + if (isOfScheme(Schemes.vscode, link) || isOfScheme(Schemes['vscode-insiders'], link)) { + return normalizeLink(vscode.Uri.parse(link).with({ scheme: vscode.env.uriScheme }).toString()); + } + // If original link doesn't look like a url with a scheme, assume it must be a link to a file in workspace if (!/^[a-z\-]+:/i.test(link)) { // Use a fake scheme for parsing @@ -261,7 +266,11 @@ export class MarkdownEngine { const validateLink = md.validateLink; md.validateLink = (link: string) => { // support file:// links - return validateLink(link) || isOfScheme(Schemes.file, link) || /^data:image\/.*?;/.test(link); + return validateLink(link) + || isOfScheme(Schemes.file, link) + || isOfScheme(Schemes.vscode, link) + || isOfScheme(Schemes['vscode-insiders'], link) + || /^data:image\/.*?;/.test(link); }; } diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index cb3228cc8be..74383466f72 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -13,7 +13,7 @@ export const Schemes = { data: 'data:', vscode: 'vscode:', 'vscode-insiders': 'vscode-insiders:', - 'vscode-resource': 'vscode-resource', + 'vscode-resource': 'vscode-resource:', }; const knownSchemes = [ From b53811e67e65c6a564a80e1c412ca2b13de02907 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Oct 2019 08:20:25 +0200 Subject: [PATCH 71/89] fix npm postinstall step, remove xterm from version matching --- build/npm/postinstall.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 87eeb860e0e..e415552b3a5 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -82,16 +82,11 @@ if (fs.existsSync(processTreeDts)) { } function getInstalledVersion(packageName, cwd) { - const opts = {}; - if (cwd) { - opts.cwd = cwd; - } - - const result = cp.spawnSync(yarn, ['list', '--pattern', packageName], opts); - const stdout = result.stdout.toString(); + const { stdout } = cp.spawnSync(yarn, ['list', packageName], { encoding: 'utf8', cwd }); const match = stdout.match(new RegExp(packageName + '@(\\S+)')); + if (!match || !match[1]) { - throw new Error('Unexpected output from yarn list: ' + stdout); + throw new Error(`Missing ${packageName} in ${cwd}: \n${stdout}`); } return match[1]; @@ -108,7 +103,7 @@ function assertSameVersionsBetweenFolders(packageName, otherFolder) { // Check that modules in both the base package.json and remote/ have the same version installed const requireSameVersionsInRemote = [ - 'xterm', + // 'xterm', 'xterm-addon-search', 'xterm-addon-web-links', 'node-pty', From a1561eb79b6d7a9f286c0165ffaca67943c8eec1 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Thu, 24 Oct 2019 02:30:54 -0400 Subject: [PATCH 72/89] Adds window border support to splash --- .../electron-browser/workbench/workbench.js | 36 ++++++++++++------- src/vs/workbench/browser/layout.ts | 8 +++++ src/vs/workbench/browser/media/style.css | 1 - .../partsSplash.contribution.ts | 3 ++ .../services/layout/browser/layoutService.ts | 10 ++++++ .../workbench/test/workbenchTestServices.ts | 8 +++++ 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 2a2c6981c7d..022394edd75 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -33,17 +33,17 @@ bootstrapWindow.load([ return require('vs/workbench/electron-browser/desktop.main').main(configuration); }); }, { - removeDeveloperKeybindingsAfterLoad: true, - canModifyDOM: function (windowConfig) { - showPartsSplash(windowConfig); - }, - beforeLoaderConfig: function (windowConfig, loaderConfig) { - loaderConfig.recordStats = true; - }, - beforeRequire: function () { - perf.mark('willLoadWorkbenchMain'); - } - }); + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + showPartsSplash(windowConfig); + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } +}); /** * @param {{ @@ -84,7 +84,7 @@ function showPartsSplash(configuration) { style.className = 'initialShellColors'; document.head.appendChild(style); document.body.className = baseTheme; - style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; }`; + style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; if (data && data.layoutInfo) { // restore parts if possible (we might not always store layout info) @@ -92,6 +92,18 @@ function showPartsSplash(configuration) { const splash = document.createElement('div'); splash.id = id; + if (layoutInfo.windowBorder) { + splash.style.position = 'relative'; + splash.style.height = 'calc(100vh - 2px)'; + splash.style.width = 'calc(100vw - 2px)'; + splash.style.border = '1px solid var(--window-border-color)'; + splash.style.setProperty('--window-border-color', colorInfo.windowBorder); + + if (layoutInfo.windowBorderRadius) { + splash.style.borderRadius = layoutInfo.windowBorderRadius; + } + } + // ensure there is enough space layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 49ccfa95402..24ebadeba87 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1146,6 +1146,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + hasWindowBorder(): boolean { + return this.state.windowBorder; + } + + getWindowBorderRadius(): string | undefined { + return this.state.windowBorder && isMacintosh ? '5px' : undefined; + } + isPanelMaximized(): boolean { if (!this.workbenchGrid) { return false; diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 4218aec3c9f..d7271d7084f 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -56,7 +56,6 @@ body.web { } .monaco-workbench.border { - position: relative; height: calc(100vh - 2px); width: calc(100vw - 2px); border: 1px solid var(--window-border-color); diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index a4f15c50597..7e511a8449d 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -80,6 +80,7 @@ class PartsSplash { sideBarBackground: this._getThemeColor(themes.SIDE_BAR_BACKGROUND), statusBarBackground: this._getThemeColor(themes.STATUS_BAR_BACKGROUND), statusBarNoFolderBackground: this._getThemeColor(themes.STATUS_BAR_NO_FOLDER_BACKGROUND), + windowBorder: this._getThemeColor(themes.WINDOW_ACTIVE_BORDER) ?? this._getThemeColor(themes.WINDOW_INACTIVE_BORDER) }; const layoutInfo = !this._shouldSaveLayoutInfo() ? undefined : { sideBarSide: this._layoutService.getSideBarPosition() === Position.RIGHT ? 'right' : 'left', @@ -88,6 +89,8 @@ class PartsSplash { activityBarWidth: this._layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? getTotalWidth(assertIsDefined(this._layoutService.getContainer(Parts.ACTIVITYBAR_PART))) : 0, sideBarWidth: this._layoutService.isVisible(Parts.SIDEBAR_PART) ? getTotalWidth(assertIsDefined(this._layoutService.getContainer(Parts.SIDEBAR_PART))) : 0, statusBarHeight: this._layoutService.isVisible(Parts.STATUSBAR_PART) ? getTotalHeight(assertIsDefined(this._layoutService.getContainer(Parts.STATUSBAR_PART))) : 0, + windowBorder: this._layoutService.hasWindowBorder(), + windowBorderRadius: this._layoutService.getWindowBorderRadius() }; this._textFileService.write( URI.file(join(this._envService.userDataPath, 'rapid_render.json')), diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index a297b2a4cb6..616d872bf57 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -114,6 +114,16 @@ export interface IWorkbenchLayoutService extends ILayoutService { */ toggleMaximizedPanel(): void; + /** + * Returns true if the window has a border. + */ + hasWindowBorder(): boolean; + + /** + * Returns the window border radius if any. + */ + getWindowBorderRadius(): string | undefined; + /** * Returns true if the panel is maximized. */ diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 5d316a1825f..2014a34180f 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -481,6 +481,14 @@ export class TestLayoutService implements IWorkbenchLayoutService { return false; } + public hasWindowBorder(): boolean { + return false; + } + + public getWindowBorderRadius(): string | undefined { + return undefined; + } + public isVisible(_part: Parts): boolean { return true; } From 67c8c940c6cffac43c60bbeb81e00b7b1811610b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 09:54:25 +0200 Subject: [PATCH 73/89] node nodejs specific test tweaks --- src/vs/base/test/node/uri.test.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index e4ad09abe26..ab592fbb385 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -575,10 +575,10 @@ suite('URI', () => { assertUriFromFsPath('c:\\test with %\\path'); assertUriFromFsPath('c:\\test with %25\\path'); assertUriFromFsPath('c:\\test with %25\\c#code'); - assertUriFromFsPath('\\\\shäres\\path\\c#\\plugin.json'); - assertUriFromFsPath('\\\\localhost\\c$\\GitDevelopment\\express'); - assertUriFromFsPath('\\\\shares'); - assertUriFromFsPath('\\\\shares\\'); + // assertUriFromFsPath('\\\\shäres\\path\\c#\\plugin.json'); // nodejs doesn't accept UNC paths as paths + // assertUriFromFsPath('\\\\localhost\\c$\\GitDevelopment\\express'); + // assertUriFromFsPath('\\\\shares'); + // assertUriFromFsPath('\\\\shares\\'); } else { assertUriFromFsPath('/foo/bar'); assertUriFromFsPath('/foo/%2e.txt'); @@ -604,10 +604,10 @@ suite('URI', () => { assertFsPathFromUri('file:///c:/bar/foo'); assertFsPathFromUri('file:///c:/alex.txt'); assertFsPathFromUri('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins'); - assertFsPathFromUri('file://unc-host/foö/bar'); - assertFsPathFromUri('file://unc-host/'); - assertFsPathFromUri('file://monacotools/folder/isi.txt'); - assertFsPathFromUri('file://monacotools1/certificates/SSL/'); + assertFsPathFromUri('file://unc-host/foö/bar', false); + // assertFsPathFromUri('file://unc-host/', false); //nodejs \\unc-host\ vs code \ + assertFsPathFromUri('file://monacotools/folder/isi.txt', false); + assertFsPathFromUri('file://monacotools1/certificates/SSL/', false); } else { assertFsPathFromUri('file:///foo/bar'); assertFsPathFromUri('file:///fo%25/bar'); @@ -636,7 +636,7 @@ suite('URI', () => { assertToString('scheme://authority/path'); assertToString('scheme:/path'); assertToString('foo:bar/path'); - // assertToString('http:/api/files/test.me?t=1234'); // URL make api the hostname, + // assertToString('http:/api/files/test.me?t=1234'); // URL makes api the hostname, assertToString('http://api/files/test.me?t=1234'); // assertToString('file:///c:/test/me'); // we encode the colon // assertToString('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins/c%23/plugin.json'); @@ -644,8 +644,7 @@ suite('URI', () => { assertToString('file://shares/files/c%23/p.cs'); assertToString('inmemory:'); assertToString('foo:api/files/test'); - assertToString('file:?q'); - assertToString('file:#d'); + assertToString('f3ile:?q'); assertToString('f3ile:#d'); assertToString('foo+bar:path'); assertToString('foo-bar:path'); From 57c8495d9428cfadfb1ccf1deb66a977597e7774 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Oct 2019 10:14:56 +0200 Subject: [PATCH 74/89] cleanup workspace editing service --- .../services/backup/common/backup.ts | 6 --- .../backup/electron-browser/backup.ts | 12 ++++++ .../electron-browser/environmentService.ts | 2 +- .../abstractWorkspaceEditingService.ts | 42 +++--------------- .../browser/workspaceEditingService.ts | 21 +++++---- .../workspaces/browser/workspacesService.ts | 10 +---- .../workspaceEditingService.ts | 43 ++++++++++++++++--- 7 files changed, 69 insertions(+), 67 deletions(-) create mode 100644 src/vs/workbench/services/backup/electron-browser/backup.ts diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 0f13f6c8a42..61f89319ae7 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -6,8 +6,6 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { joinPath, relativePath } from 'vs/base/common/resources'; export const IBackupFileService = createDecorator('backupFileService'); @@ -88,7 +86,3 @@ export interface IBackupFileService { */ discardAllWorkspaceBackups(): Promise; } - -export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: IEnvironmentService): URI { - return joinPath(environmentService.userRoamingDataHome, relativePath(URI.file(environmentService.userDataPath), URI.file(backupWorkspacePath))!); -} diff --git a/src/vs/workbench/services/backup/electron-browser/backup.ts b/src/vs/workbench/services/backup/electron-browser/backup.ts new file mode 100644 index 00000000000..6d6bfd34246 --- /dev/null +++ b/src/vs/workbench/services/backup/electron-browser/backup.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { joinPath, relativePath } from 'vs/base/common/resources'; + +export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: IEnvironmentService): URI { + return joinPath(environmentService.userRoamingDataHome, relativePath(URI.file(environmentService.userDataPath), URI.file(backupWorkspacePath))!); +} diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index c480a63aafc..d1c4dc00fe0 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -9,7 +9,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { memoize } from 'vs/base/common/decorators'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; +import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/electron-browser/backup'; import { join } from 'vs/base/common/path'; import { IDebugParams } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index ab1ad090bf4..f33be4ad9f0 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -8,14 +8,10 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IBackupFileService, toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; -import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; import { isEqual, getComparisonKey } from 'vs/base/common/resources'; @@ -36,9 +32,6 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IWorkspaceContextService private readonly contextService: WorkspaceService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageService private readonly storageService: IStorageService, - @IExtensionService private readonly extensionService: IExtensionService, - @IBackupFileService private readonly backupFileService: IBackupFileService, @INotificationService private readonly notificationService: INotificationService, @ICommandService private readonly commandService: ICommandService, @IFileService private readonly fileService: IFileService, @@ -267,7 +260,9 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi ); } - async enterWorkspace(path: URI): Promise { + abstract async enterWorkspace(path: URI): Promise; + + protected async doEnterWorkspace(path: URI): Promise { if (!!this.environmentService.extensionTestsLocationURI) { throw new Error('Entering a new workspace is not possible in tests.'); } @@ -282,34 +277,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi const workspaceImpl = this.contextService as WorkspaceService; await workspaceImpl.initialize(workspace); - const result = await this.workspacesService.enterWorkspace(path); - if (result) { - - // Migrate storage to new workspace - await this.migrateStorage(result.workspace); - - // Reinitialize backup service - this.environmentService.configuration.backupPath = result.backupPath; - this.environmentService.configuration.backupWorkspaceResource = result.backupPath ? toBackupWorkspaceResource(result.backupPath, this.environmentService) : undefined; - if (this.backupFileService instanceof BackupFileService) { - this.backupFileService.reinitialize(); - } - } - - // TODO@aeschli: workaround until restarting works - if (this.environmentService.configuration.remoteAuthority) { - this.hostService.reload(); - } - - // Restart the extension host: entering a workspace means a new location for - // storage and potentially a change in the workspace.rootPath property. - else { - this.extensionService.restartExtensionHost(); - } - } - - private migrateStorage(toWorkspace: IWorkspaceIdentifier): Promise { - return this.storageService.migrate(toWorkspace); + return this.workspacesService.enterWorkspace(path); } private migrateWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): Promise { diff --git a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index 5a6500429f9..7a6c8ba6df9 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -5,11 +5,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; @@ -21,6 +18,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { URI } from 'vs/base/common/uri'; export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingService { @@ -30,9 +28,6 @@ export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingServ @IJSONEditingService jsonEditingService: IJSONEditingService, @IWorkspaceContextService contextService: WorkspaceService, @IConfigurationService configurationService: IConfigurationService, - @IStorageService storageService: IStorageService, - @IExtensionService extensionService: IExtensionService, - @IBackupFileService backupFileService: IBackupFileService, @INotificationService notificationService: INotificationService, @ICommandService commandService: ICommandService, @IFileService fileService: IFileService, @@ -43,9 +38,17 @@ export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingServ @IDialogService dialogService: IDialogService, @IHostService hostService: IHostService ) { - super(jsonEditingService, contextService, configurationService, storageService, extensionService, backupFileService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService); + super(jsonEditingService, contextService, configurationService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService); + } + + async enterWorkspace(path: URI): Promise { + const result = await this.doEnterWorkspace(path); + if (result) { + + // Open workspace in same window + await this.hostService.openWindow([{ workspaceUri: path }], { forceReuseWindow: true, noRecentEntry: isUntitledWorkspace(path, this.environmentService) }); + } } } registerSingleton(IWorkspaceEditingService, BrowserWorkspaceEditingService, true); - diff --git a/src/vs/workbench/services/workspaces/browser/workspacesService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts index 1fdd11779b6..653542ce912 100644 --- a/src/vs/workbench/services/workspaces/browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -12,7 +12,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { joinPath } from 'vs/base/common/resources'; @@ -31,7 +30,6 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS @IStorageService private readonly storageService: IStorageService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @ILogService private readonly logService: ILogService, - @IHostService private readonly hostService: IHostService, @IFileService private readonly fileService: IFileService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { @@ -125,13 +123,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS //#region Workspace Management async enterWorkspace(path: URI): Promise { - - // Open workspace in same window - await this.hostService.openWindow([{ workspaceUri: path }], { forceReuseWindow: true, noRecentEntry: isUntitledWorkspace(path, this.environmentService) }); - - return { - workspace: await this.getWorkspaceIdentifier(path) - }; + return { workspace: await this.getWorkspaceIdentifier(path) }; } async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { diff --git a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts index 08edca2f094..63e66f3a6b5 100644 --- a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts @@ -8,11 +8,12 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspacesService, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, isUntitledWorkspace, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/electron-browser/backup'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { isEqual, basename } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -29,6 +30,7 @@ import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspace import { IElectronService } from 'vs/platform/electron/node/electron'; import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingService { @@ -39,9 +41,9 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi @IWorkspaceContextService contextService: WorkspaceService, @IElectronService private electronService: IElectronService, @IConfigurationService configurationService: IConfigurationService, - @IStorageService storageService: IStorageService, - @IExtensionService extensionService: IExtensionService, - @IBackupFileService backupFileService: IBackupFileService, + @IStorageService private storageService: IStorageService, + @IExtensionService private extensionService: IExtensionService, + @IBackupFileService private backupFileService: IBackupFileService, @INotificationService notificationService: INotificationService, @ICommandService commandService: ICommandService, @IFileService fileService: IFileService, @@ -54,7 +56,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi @ILabelService private readonly labelService: ILabelService, @IHostService hostService: IHostService, ) { - super(jsonEditingService, contextService, configurationService, storageService, extensionService, backupFileService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService); + super(jsonEditingService, contextService, configurationService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, hostService); this.registerListeners(); } @@ -165,6 +167,37 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi return true; // OK } + + async enterWorkspace(path: URI): Promise { + const result = await this.doEnterWorkspace(path); + if (result) { + + // Migrate storage to new workspace + await this.migrateStorage(result.workspace); + + // Reinitialize backup service + this.environmentService.configuration.backupPath = result.backupPath; + this.environmentService.configuration.backupWorkspaceResource = result.backupPath ? toBackupWorkspaceResource(result.backupPath, this.environmentService) : undefined; + if (this.backupFileService instanceof BackupFileService) { + this.backupFileService.reinitialize(); + } + } + + // TODO@aeschli: workaround until restarting works + if (this.environmentService.configuration.remoteAuthority) { + this.hostService.reload(); + } + + // Restart the extension host: entering a workspace means a new location for + // storage and potentially a change in the workspace.rootPath property. + else { + this.extensionService.restartExtensionHost(); + } + } + + private migrateStorage(toWorkspace: IWorkspaceIdentifier): Promise { + return this.storageService.migrate(toWorkspace); + } } registerSingleton(IWorkspaceEditingService, NativeWorkspaceEditingService, true); From b8bb5e71b104ede069daa46366bdabc447b327b2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Oct 2019 10:15:15 +0200 Subject: [PATCH 75/89] Cleanup src/vs/platform/environment/node/argv.ts. Fixes #76239 --- src/vs/code/electron-main/main.ts | 4 +-- src/vs/code/node/cli.ts | 5 +-- src/vs/code/test/node/argv.test.ts | 3 +- src/vs/platform/environment/node/argv.ts | 35 +------------------ .../platform/environment/node/argvHelper.ts | 14 ++++++++ .../environment/node/waitMarkerFile.ts | 28 +++++++++++++++ 6 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 src/vs/platform/environment/node/waitMarkerFile.ts diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 9a4f97020eb..bb05eddc6ce 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -8,8 +8,8 @@ import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; -import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; -import { addArg, createWaitMarkerFile } from 'vs/platform/environment/node/argv'; +import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; +import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/node/paths'; import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 9e814c1b58b..71efcf3bffd 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -6,8 +6,9 @@ import * as os from 'os'; import * as fs from 'fs'; import { spawn, ChildProcess, SpawnOptions } from 'child_process'; -import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile, OPTIONS } from 'vs/platform/environment/node/argv'; -import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; +import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; +import { parseCLIProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; +import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; import * as paths from 'vs/base/common/path'; diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/code/test/node/argv.test.ts index 59911ebc41e..79ce7d22445 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/code/test/node/argv.test.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { formatOptions, Option, addArg } from 'vs/platform/environment/node/argv'; +import { formatOptions, Option } from 'vs/platform/environment/node/argv'; +import { addArg } from 'vs/platform/environment/node/argvHelper'; suite('formatOptions', () => { diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3721d14c40e..33a2671982a 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -7,13 +7,10 @@ import * as minimist from 'vscode-minimist'; import * as os from 'os'; import { localize } from 'vs/nls'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { join } from 'vs/base/common/path'; -import { writeFileSync } from 'vs/base/node/pfs'; /** * This code is also used by standalone cli's. Avoid adding any other dependencies. */ - const helpCategories = { o: localize('optionsUpperCase', "Options"), e: localize('extensionsManagement', "Extensions Management"), @@ -161,7 +158,7 @@ export function parseArgs(args: string[], options: OptionDescriptions, err } } } - // remote aliases to avoid confusion + // remove aliases to avoid confusion const parsedArgs = minimist(args, { string, boolean, alias }); const cleanedArgs: any = {}; @@ -310,33 +307,3 @@ export function buildVersionMessage(version: string | undefined, commit: string return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; } -export function addArg(argv: string[], ...args: string[]): string[] { - const endOfArgsMarkerIndex = argv.indexOf('--'); - if (endOfArgsMarkerIndex === -1) { - argv.push(...args); - } else { - // if the we have an argument "--" (end of argument marker) - // we cannot add arguments at the end. rather, we add - // arguments before the "--" marker. - argv.splice(endOfArgsMarkerIndex, 0, ...args); - } - - return argv; -} - -export function createWaitMarkerFile(verbose?: boolean): string | undefined { - const randomWaitMarkerPath = join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); - - try { - writeFileSync(randomWaitMarkerPath, ''); - if (verbose) { - console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); - } - return randomWaitMarkerPath; - } catch (err) { - if (verbose) { - console.error(`Failed to create marker file for --wait: ${err}`); - } - return undefined; - } -} diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index 1dd2f688934..2aef6dff05c 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -69,3 +69,17 @@ export function parseCLIProcessArgv(processArgv: string[]): ParsedArgs { return parseAndValidate(args, true); } + +export function addArg(argv: string[], ...args: string[]): string[] { + const endOfArgsMarkerIndex = argv.indexOf('--'); + if (endOfArgsMarkerIndex === -1) { + argv.push(...args); + } else { + // if the we have an argument "--" (end of argument marker) + // we cannot add arguments at the end. rather, we add + // arguments before the "--" marker. + argv.splice(endOfArgsMarkerIndex, 0, ...args); + } + + return argv; +} diff --git a/src/vs/platform/environment/node/waitMarkerFile.ts b/src/vs/platform/environment/node/waitMarkerFile.ts new file mode 100644 index 00000000000..8916ef40732 --- /dev/null +++ b/src/vs/platform/environment/node/waitMarkerFile.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small. + */ +import * as path from 'vs/base/common/path'; +import * as os from 'os'; +import * as fs from 'fs'; + +export function createWaitMarkerFile(verbose?: boolean): string | undefined { + const randomWaitMarkerPath = path.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + + try { + fs.writeFileSync(randomWaitMarkerPath, ''); // use built-in fs to avoid dragging in more dependencies + if (verbose) { + console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); + } + return randomWaitMarkerPath; + } catch (err) { + if (verbose) { + console.error(`Failed to create marker file for --wait: ${err}`); + } + return undefined; + } +} From 915d37dea92aa8b99ac291793370c061dbcb1d2b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Oct 2019 10:28:13 +0200 Subject: [PATCH 76/89] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1800d19ef9d..5bb3168f8a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.40.0", - "distro": "9409a98a9808db59c1ed02f354c4f3d5bba662ea", + "distro": "f0585fa45498242d599deeb2bb18393f704f25d9", "author": { "name": "Microsoft Corporation" }, From bbb516cb6afddde97b735627c130520bd92fd091 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 10:43:18 +0200 Subject: [PATCH 77/89] polish, extract util functions, fix issue with overeager hex detection --- src/vs/base/common/strings.ts | 30 +++++++++++++++- src/vs/base/common/uri.ts | 52 ++++++++++++--------------- src/vs/base/test/node/uri.test.ts | 58 ++++++++++++++++--------------- 3 files changed, 81 insertions(+), 59 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 78cf46bd2c3..a9d88083981 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -338,6 +338,34 @@ export function compareIgnoreCase(a: string, b: string): number { } } +/** + * [0-9] + */ +export function isAsciiDigit(code: number): boolean { + return code >= CharCode.Digit0 && code <= CharCode.Digit9; +} + +/** + * [a-f] + */ +export function isLowerAsciiHex(code: number): boolean { + return code >= CharCode.a && code <= CharCode.f; +} + +/** + * [A-F] + */ +export function isUpperAsciiHex(code: number): boolean { + return code >= CharCode.A && code <= CharCode.F; +} + +/** + * [0-9a-fA-F] + */ +export function isAsciiHex(code: number): boolean { + return isAsciiDigit(code) || isLowerAsciiHex(code) || isUpperAsciiHex(code); +} + export function isLowerAsciiLetter(code: number): boolean { return code >= CharCode.a && code <= CharCode.z; } @@ -346,7 +374,7 @@ export function isUpperAsciiLetter(code: number): boolean { return code >= CharCode.A && code <= CharCode.Z; } -function isAsciiLetter(code: number): boolean { +export function isAsciiLetter(code: number): boolean { return isLowerAsciiLetter(code) || isUpperAsciiLetter(code); } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index ca77c3b5525..c122ad20127 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -5,7 +5,7 @@ import { isWindows } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; -import { isHighSurrogate, isLowSurrogate } from 'vs/base/common/strings'; +import { isHighSurrogate, isLowSurrogate, isLowerAsciiHex, isAsciiLetter } from 'vs/base/common/strings'; const _schemeRegExp = /^\w[\w\d+.-]*$/; @@ -207,10 +207,7 @@ export class URI implements UriComponents { * with URIs that represent files on disk (`file` scheme). */ get fsPath(): string { - // if (this.scheme !== 'file') { - // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`); - // } - return _makeFsPath(this); + return _toFsPath(this.scheme, this.authority, this.path); } // ---- modify to new ------------------------- @@ -287,7 +284,7 @@ export class URI implements UriComponents { percentDecode(query), percentDecode(fragment), ); - result._formatted = _toString(_normalEncoder, scheme, authority, path, query, fragment); + result._external = _toExternal(_normalEncoder, scheme, authority, path, query, fragment); return result; } @@ -375,7 +372,7 @@ export class URI implements UriComponents { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding: boolean = false): string { - return _toString(skipEncoding ? _minimalEncoder : _normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toExternal(skipEncoding ? _minimalEncoder : _normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } toJSON(): UriComponents { @@ -393,7 +390,7 @@ export class URI implements UriComponents { return data; } else { const result = new _URI(data); - result._formatted = (data).external; + result._external = (data).external; result._fsPath = (data)._sep === _pathSepMarker ? (data).fsPath : null; return result; } @@ -420,12 +417,12 @@ const _pathSepMarker = isWindows ? 1 : undefined; // tslint:disable-next-line:class-name class _URI extends URI { - _formatted: string | null = null; + _external: string | null = null; _fsPath: string | null = null; get fsPath(): string { if (!this._fsPath) { - this._fsPath = _makeFsPath(this); + this._fsPath = _toFsPath(this.scheme, this.authority, this.path); } return this._fsPath; } @@ -433,12 +430,12 @@ class _URI extends URI { toString(skipEncoding: boolean = false): string { if (skipEncoding) { // we don't cache that - return _toString(_minimalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); + return _toExternal(_minimalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } - if (!this._formatted) { - this._formatted = _toString(_normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); + if (!this._external) { + this._external = _toExternal(_normalEncoder, this.scheme, this.authority, this.path, this.query, this.fragment); } - return this._formatted; + return this._external; } toJSON(): UriComponents { @@ -450,8 +447,8 @@ class _URI extends URI { res.fsPath = this._fsPath; res._sep = _pathSepMarker; } - if (this._formatted) { - res.external = this._formatted; + if (this._external) { + res.external = this._external; } // uri components if (this.path) { @@ -476,22 +473,22 @@ class _URI extends URI { /** * Compute `fsPath` for the given uri */ -function _makeFsPath(uri: URI): string { +function _toFsPath(scheme: string, authority: string, path: string): string { let value: string; - if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + if (authority && path.length > 1 && scheme === 'file') { // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uri.path}`; + value = `//${authority}${path}`; } else if ( - uri.path.charCodeAt(0) === CharCode.Slash - && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) - && uri.path.charCodeAt(2) === CharCode.Colon + path.charCodeAt(0) === CharCode.Slash + && isAsciiLetter(path.charCodeAt(1)) + && path.charCodeAt(2) === CharCode.Colon ) { // windows drive letter: file:///c:/far/boo - value = uri.path[1].toLowerCase() + uri.path.substr(2); + value = path[1].toLowerCase() + path.substr(2); } else { // other path - value = uri.path; + value = path; } if (isWindows) { value = value.replace(_slashRegExp, '\\'); @@ -562,11 +559,6 @@ function isHashOrQuestionMark(code: number): boolean { return code === CharCode.Hash || code === CharCode.QuestionMark; } -function isLowerAsciiHex(code: number): boolean { - return code >= CharCode.Digit0 && code <= CharCode.Digit9 - || code >= CharCode.a && code <= CharCode.z; -} - const _encodeTable: string[] = (function () { let table: string[] = []; for (let code = 0; code < 128; code++) { @@ -639,7 +631,7 @@ const _driveLetterRegExp = /^(\/?[a-z])(:|%3a)/i; /** * Create the external version of a uri */ -function _toString(encoder: { (code: number): boolean }[], scheme: string, authority: string, path: string, query: string, fragment: string): string { +function _toExternal(encoder: { (code: number): boolean }[], scheme: string, authority: string, path: string, query: string, fragment: string): string { let res = ''; if (scheme) { diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index ab592fbb385..f5550e22855 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -88,7 +88,7 @@ suite('URI', () => { assert.equal(uri2.fragment, uri3.fragment); }); - test('with, identity', () => { + test('URI#with, identity', () => { let uri = URI.parse('foo:bar/path'); let uri2 = uri.with(null!); @@ -101,7 +101,7 @@ suite('URI', () => { assert.ok(uri === uri2); }); - test('with, changes', () => { + test('URI#with, changes', () => { assert.equal(URI.parse('before:some/file/path').with({ scheme: 'after' }).toString(), 'after:some/file/path'); assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t=1234'); assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t=1234'); @@ -111,7 +111,7 @@ suite('URI', () => { assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t=1234'); }); - test('with, remove components #8465', () => { + test('URI#with, remove components #8465', () => { assert.equal(URI.parse('scheme://authority/path').with({ authority: '' }).toString(), 'scheme:/path'); assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ authority: '' }).toString(), 'scheme:/path'); assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ authority: null }).toString(), 'scheme:/path'); @@ -121,7 +121,7 @@ suite('URI', () => { assert.equal(URI.parse('scheme:/path').with({ authority: null }).toString(), 'scheme:/path'); }); - test('with, validation', () => { + test('URI#with, validation', () => { let uri = URI.parse('foo:bar/path'); assert.throws(() => uri.with({ scheme: 'fai:l' })); assert.throws(() => uri.with({ scheme: 'fäil' })); @@ -310,50 +310,52 @@ suite('URI', () => { assert.equal(value.toString(), 'file:///a.file'); }); + + function assertToString(input: string | URI, expected: string) { + if (typeof input === 'string') { + input = URI.parse(input); + } + const actual = input.toString(); + assert.equal(actual, expected.toString()); + } + test('URI.toString, only scheme and query', () => { - const value = URI.parse('stuff:?qüery'); - assert.equal(value.toString(), 'stuff:?q%C3%BCery'); + assertToString('stuff:?qüery', 'stuff:?q%C3%BCery'); }); test('URI#toString, upper-case percent espaces', () => { - const value = URI.parse('file://sh%c3%a4res/path'); - assert.equal(value.toString(), 'file://sh%C3%A4res/path'); + assertToString('file://sh%c3%a4res/path', 'file://sh%C3%A4res/path'); + assertToString('file://sh%c3%z4res/path', 'file://sh%C3%z4res/path'); + assertToString('file:///sh%a0res/path', 'file:///sh%A0res/path'); // also upper-cased invalid sequence }); test('URI#toString, lower-case windows drive letter', () => { - assert.equal(URI.parse('untitled:c:/Users/jrieken/Code/abc.txt').toString(), 'untitled:c%3A/Users/jrieken/Code/abc.txt'); - assert.equal(URI.parse('untitled:C:/Users/jrieken/Code/abc.txt').toString(), 'untitled:c%3A/Users/jrieken/Code/abc.txt'); + assertToString('untitled:c:/Users/jrieken/Code/abc.txt', 'untitled:c%3A/Users/jrieken/Code/abc.txt'); + assertToString('untitled:C:/Users/jrieken/Code/abc.txt', 'untitled:c%3A/Users/jrieken/Code/abc.txt'); }); test('URI#toString, escape all the bits', () => { - const value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); - assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20+%20Other%20Th%C3%AEng%C3%9F/model.js'); + assertToString(value, 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20+%20Other%20Th%C3%AEng%C3%9F/model.js'); }); test('URI#toString, don\'t encode port', () => { let value = URI.parse('http://localhost:8080/far'); - assert.equal(value.toString(), 'http://localhost:8080/far'); + assertToString(value, 'http://localhost:8080/far'); value = URI.from({ scheme: 'http', authority: 'löcalhost:8080', path: '/far', query: undefined, fragment: undefined }); - assert.equal(value.toString(), 'http://l%C3%B6calhost:8080/far'); + assertToString(value, 'http://l%C3%B6calhost:8080/far'); }); test('URI#toString, user information in authority', () => { - let value = URI.parse('http://foo:bar@localhost/far'); - assert.equal(value.toString(), 'http://foo:bar@localhost/far'); - - value = URI.parse('http://foo@localhost/far'); - assert.equal(value.toString(), 'http://foo@localhost/far'); - - value = URI.parse('http://foo:bAr@localhost:8080/far'); - assert.equal(value.toString(), 'http://foo:bAr@localhost:8080/far'); - - value = URI.parse('http://foo@localhost:8080/far'); - assert.equal(value.toString(), 'http://foo@localhost:8080/far'); - - value = URI.from({ scheme: 'http', authority: 'föö:bör@löcalhost:8080', path: '/far', query: undefined, fragment: undefined }); - assert.equal(value.toString(), 'http://f%C3%B6%C3%B6:b%C3%B6r@l%C3%B6calhost:8080/far'); + assertToString('http://foo:bar@localhost/far', 'http://foo:bar@localhost/far'); + assertToString('http://foo@localhost/far', 'http://foo@localhost/far'); + assertToString('http://foo:bAr@localhost:8080/far', 'http://foo:bAr@localhost:8080/far'); + assertToString('http://foo@localhost:8080/far', 'http://foo@localhost:8080/far'); + assertToString( + URI.from({ scheme: 'http', authority: 'föö:bör@löcalhost:8080', path: '/far', query: undefined, fragment: undefined }), + 'http://f%C3%B6%C3%B6:b%C3%B6r@l%C3%B6calhost:8080/far' + ); }); test('correctFileUriToFilePath2', () => { From 321c275faa5fa7d9ad3958799b2bcf5ba0f77017 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Oct 2019 10:55:58 +0200 Subject: [PATCH 78/89] cleanup environment service --- .../contrib/update/browser/update.ts | 2 +- .../environment/browser/environmentService.ts | 101 ++++++++++-------- .../environment/common/environmentService.ts | 7 +- .../electron-browser/environmentService.ts | 9 +- .../services/search/node/searchService.ts | 3 +- .../telemetry/browser/telemetryService.ts | 5 +- .../textfile/browser/textFileService.ts | 7 +- .../electron-browser/nativeTextFileService.ts | 11 ++ 8 files changed, 78 insertions(+), 67 deletions(-) diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 0245d46aa5b..e0bd372a718 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -134,7 +134,7 @@ export class ProductContribution implements IWorkbenchContribution { // was there an update? if so, open release notes const releaseNotesUrl = productService.releaseNotesUrl; - if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { + if (shouldShowReleaseNotes && !environmentService.args['skip-release-notes'] && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { showReleaseNotes(instantiationService, productService.version) .then(undefined, () => { notificationService.prompt( diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 996e9ab1b19..417335c2a42 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -9,7 +9,7 @@ import { IProcessEnvironment } from 'vs/base/common/platform'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { BACKUPS, IDebugParams, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; +import { BACKUPS, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; import { LogLevel } from 'vs/platform/log/common/log'; import { IPath, IPathsToWaitFor, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -23,7 +23,7 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { constructor(private readonly options: IBrowserWorkbenchEnvironmentConstructionOptions, private readonly environment: IWorkbenchEnvironmentService) { } - //#region PROPERLY CONFIGURED + //#region PROPERLY CONFIGURED IN DESKTOP + WEB @memoize get sessionId(): string { return generateUuid(); } @@ -37,27 +37,32 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { @memoize get backupWorkspaceResource(): URI { return joinPath(this.environment.backupHome, this.options.workspaceId); } + // TODO@rachel TODO@sbatten fix me, should be stable between sessions + @memoize + get machineId(): string { return generateUuid(); } + + // Currently unsupported in web + get filesToOpenOrCreate(): IPath[] | undefined { return undefined; } + get filesToDiff(): IPath[] | undefined { return undefined; } + //#endregion - //#region TODO@ben TO BE DONE + //#region TODO MOVE TO NODE LAYER _!: any[]; - readonly machineId = generateUuid(); windowId!: number; - logLevel!: LogLevel; - mainPid!: number; + logLevel!: LogLevel; + appRoot!: string; execPath!: string; - isInitialStartup?: boolean; - - userEnv!: IProcessEnvironment; + backupPath?: string; nodeCachedDataDir?: string; - backupPath?: string; + userEnv!: IProcessEnvironment; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; @@ -69,10 +74,9 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { accessibilitySupport?: boolean; partsSplashPath?: string; + isInitialStartup?: boolean; perfEntries!: ExportData; - filesToOpenOrCreate?: IPath[]; - filesToDiff?: IPath[]; filesToWait?: IPathsToWaitFor; //#endregion @@ -99,7 +103,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment _serviceBrand: undefined; - //#region PROPERLY CONFIGURED + //#region PROPERLY CONFIGURED IN DESKTOP + WEB @memoize get isBuilt(): boolean { return !!product.commit; } @@ -175,10 +179,17 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment return this.webviewExternalEndpoint.replace('{{uuid}}', '*'); } + // Currently not configurable in web + get disableExtensions() { return false; } + get extensionsPath(): string | undefined { return undefined; } + get verbose(): boolean { return false; } + get disableUpdates(): boolean { return false; } + get logExtensionHostCommunication(): boolean { return false; } + //#endregion - //#region TODO@ben TO BE DONE + //#region TODO MOVE TO NODE LAYER private _configuration: IWindowConfiguration | undefined = undefined; get configuration(): IWindowConfiguration { @@ -189,47 +200,53 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment return this._configuration; } - readonly args = { _: [] }; - readonly appRoot = '/web/'; - - argvResource!: URI; - - extensionTestsLocationURI?: URI; - - execPath!: string; - cliPath!: string; - userHome!: string; - userDataPath!: string; - appSettingsHome!: URI; - - machineSettingsHome!: URI; - machineSettingsResource!: URI; - globalStorageHome!: string; - workspaceStorageHome!: string; - backupWorkspacesPath!: string; - - disableExtensions!: boolean | string[]; - builtinExtensionsPath!: string; - extensionsPath?: string; - debugSearch!: IDebugParams; - logExtensionHostCommunication!: boolean; + args = { _: [] }; wait!: boolean; status!: boolean; log?: string; - verbose!: boolean; - skipReleaseNotes!: boolean; mainIPCHandle!: string; sharedIPCHandle!: string; + nodeCachedDataDir?: string; - installSourcePath!: string; - disableUpdates!: boolean; + + argvResource!: URI; + disableCrashReporter!: boolean; + driverHandle?: string; driverVerbose!: boolean; + + installSourcePath!: string; + + builtinExtensionsPath!: string; + + globalStorageHome!: string; + workspaceStorageHome!: string; + + backupWorkspacesPath!: string; + + machineSettingsHome!: URI; + machineSettingsResource!: URI; + + userDataPath!: string; + appRoot!: string; + appSettingsHome!: URI; + execPath!: string; + cliPath!: string; + + //#endregion + + + //#region TODO ENABLE IN WEB + + extensionTestsLocationURI?: URI; + galleryMachineIdResource?: URI; + userHome!: string; + //#endregion constructor(readonly options: IBrowserWorkbenchEnvironmentConstructionOptions) { } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 0f7a4d8e59a..a28cfc41e4e 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -5,7 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService, IDebugParams } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { URI } from 'vs/base/common/uri'; @@ -22,12 +22,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly logFile: URI; readonly logExtensionHostCommunication: boolean; - readonly debugSearch: IDebugParams; - readonly webviewExternalEndpoint: string; readonly webviewResourceRoot: string; readonly webviewCspSource: string; - - readonly skipReleaseNotes: boolean | undefined; - } diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index d1c4dc00fe0..f6ee5070293 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EnvironmentService, parseSearchPort } from 'vs/platform/environment/node/environmentService'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { memoize } from 'vs/base/common/decorators'; @@ -11,7 +11,6 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/electron-browser/backup'; import { join } from 'vs/base/common/path'; -import { IDebugParams } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; export class NativeWorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService { @@ -31,9 +30,6 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem @memoize get webviewCspSource(): string { return 'vscode-resource:'; } - @memoize - get skipReleaseNotes(): boolean { return !!this.args['skip-release-notes']; } - @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } @@ -43,9 +39,6 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem @memoize get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } - @memoize - get debugSearch(): IDebugParams { return parseSearchPort(this.args, this.isBuilt); } - constructor( readonly configuration: IWindowConfiguration, execPath: string, diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 9b00ee68b63..161dfdfc8d0 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -26,6 +26,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { parseSearchPort } from 'vs/platform/environment/node/environmentService'; export class LocalSearchService extends SearchService { constructor( @@ -42,7 +43,7 @@ export class LocalSearchService extends SearchService { super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); - this.diskSearch = instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch); + this.diskSearch = instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, parseSearchPort(environmentService.args, environmentService.isBuilt)); } } diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index 0c62adace7c..8459af68200 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -52,11 +52,10 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (!environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { + if (!!productService.enableTelemetry) { const config: ITelemetryServiceConfig = { appender: combinedAppender(new WebTelemetryAppender(logService, remoteAgentService), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), - piiPaths: [environmentService.appRoot] + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority) }; this.impl = this._register(new BaseTelemetryService(config, configurationService)); diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 349166dabeb..b3d944bd60f 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -566,16 +566,11 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } async confirmSave(resources?: URI[]): Promise { - if (this.environmentService.isExtensionDevelopment) { - if (!this.environmentService.args['extension-development-confirm-save']) { - return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) - } - } - const resourcesToConfirm = this.getDirty(resources); if (resourcesToConfirm.length === 0) { return ConfirmResult.DONT_SAVE; } + return promptSave(this.dialogService, resourcesToConfirm); } diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 06a4e5d87c8..397b4b30f90 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -41,6 +41,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ConfirmResult } from 'vs/workbench/common/editor'; export class NativeTextFileService extends AbstractTextFileService { @@ -297,6 +298,16 @@ export class NativeTextFileService extends AbstractTextFileService { protected getWindowCount(): Promise { return this.electronService.getWindowCount(); } + + async confirmSave(resources?: URI[]): Promise { + if (this.environmentService.isExtensionDevelopment) { + if (!this.environmentService.args['extension-development-confirm-save']) { // + return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) + } + } + + return super.confirmSave(resources); + } } export interface IEncodingOverride { From 1444e61339b2e9c28ac21fdead4c60afaf49dedb Mon Sep 17 00:00:00 2001 From: Liming Jin Date: Thu, 24 Oct 2019 17:05:08 +0800 Subject: [PATCH 79/89] fix: error when filling in the HTTP proxy address in IPv6 format (#77260, fixes #77259) --- src/vs/platform/request/common/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 8e0cf20ee58..81ec255e65b 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -69,7 +69,7 @@ Registry.as(Extensions.Configuration) properties: { 'http.proxy': { type: 'string', - pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+)(:\\d+)?/?$|^$', + pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$', markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables.") }, 'http.proxyStrictSSL': { From b5652be1d7883eb1e8201593cb76f503d15f922f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Oct 2019 11:09:27 +0200 Subject: [PATCH 80/89] Have `*` terminate a link only when the character before the link is also a `*` --- src/vs/editor/common/modes/linkComputer.ts | 3 ++- src/vs/editor/test/common/modes/linkComputer.test.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 0bb9c7684d6..9d381736536 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -265,7 +265,8 @@ export class LinkComputer { chClass = (linkBeginChCode === CharCode.SingleQuote || linkBeginChCode === CharCode.DoubleQuote) ? CharacterClass.None : CharacterClass.ForceTermination; break; case CharCode.Asterisk: - chClass = CharacterClass.ForceTermination; + // `*` terminates a link if the link began with `*` + chClass = (linkBeginChCode === CharCode.Asterisk) ? CharacterClass.ForceTermination : CharacterClass.None; break; default: chClass = classifier.get(chCode); diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts index 52db6612ce0..ebb5df027a5 100644 --- a/src/vs/editor/test/common/modes/linkComputer.test.ts +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -185,7 +185,7 @@ suite('Editor Modes - Link Computer', () => { ); assertLink( 'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', - ' http://* ' + ' http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items ' ); }); @@ -202,4 +202,11 @@ suite('Editor Modes - Link Computer', () => { ' http://[::1]:5000/connect/token ' ); }); + + test('issue #70254: bold links dont open in markdown file using editor mode with ctrl + click', () => { + assertLink( + '2. Navigate to **https://portal.azure.com**', + ' https://portal.azure.com ' + ); + }); }); From e0617e58537da99f88da183da5002390e92d253c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 11:12:28 +0200 Subject: [PATCH 81/89] some more test utils --- src/vs/base/test/node/uri.test.ts | 66 ++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index f5550e22855..de2bcd12348 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -319,6 +319,32 @@ suite('URI', () => { assert.equal(actual, expected.toString()); } + function assertComponents(input: string | URI, scheme: string, authority: string, path: string, query: string, fragment: string) { + if (typeof input === 'string') { + input = URI.parse(input); + } + assert.equal(input.scheme, scheme); + assert.equal(input.authority, authority); + assert.equal(input.path, path); + assert.equal(input.query, query); + assert.equal(input.fragment, fragment); + } + + function assertEqualUri(input: string | URI, other: string | URI) { + if (typeof input === 'string') { + input = URI.parse(input); + } + if (typeof other === 'string') { + other = URI.parse(other); + } + assert.equal(input.scheme, other.scheme); + assert.equal(input.authority, other.authority); + assert.equal(input.path, other.path); + assert.equal(input.query, other.query); + assert.equal(input.fragment, other.fragment); + assert.equal(input.toString(), other.toString()); + } + test('URI.toString, only scheme and query', () => { assertToString('stuff:?qüery', 'stuff:?q%C3%BCery'); }); @@ -450,40 +476,35 @@ suite('URI', () => { test('Links in markdown are broken if url contains encoded parameters #79474', function () { let strIn = 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'; let uri1 = URI.parse(strIn); + assertToString(uri1, strIn); let strOut = uri1.toString(); let uri2 = URI.parse(strOut); - - assert.equal(uri1.scheme, uri2.scheme); - assert.equal(uri1.authority, uri2.authority); - assert.equal(uri1.path, uri2.path); - assert.equal(uri1.query, uri2.query); - assert.equal(uri1.fragment, uri2.fragment); + assertEqualUri(uri1, uri2); assert.equal(strIn, strOut); }); test('Uri#parse can break path-component #45515', function () { let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; let uri1 = URI.parse(strIn); + assertToString(uri1, strIn); + + assertComponents(uri1, + 'https', + 'firebasestorage.googleapis.com', + '/v0/b/brewlangerie.appspot.com/o/products/zVNZkudXJyq8bPGTXUxx/Betterave-Sesame.jpg', // INCORRECT: %2F got decoded but for compat reasons we cannot change this anymore... + 'alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437', + '' + ); + let strOut = uri1.toString(); let uri2 = URI.parse(strOut); - - assert.equal(uri1.scheme, uri2.scheme); - assert.equal(uri1.authority, uri2.authority); - assert.equal(uri1.path, uri2.path); - assert.equal(uri1.query, uri2.query); - assert.equal(uri1.fragment, uri2.fragment); - assert.equal(strIn, strOut); + assertEqualUri(uri1, uri2); }); - test('Uri#parse can break path-component #45515, part 2', function () { - let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; - let uri1 = URI.parse(strIn); - assert.equal(uri1.toString(), strIn); - assert.equal(uri1.scheme, 'https'); - assert.equal(uri1.authority, 'firebasestorage.googleapis.com'); - assert.equal(uri1.path, '/v0/b/brewlangerie.appspot.com/o/products/zVNZkudXJyq8bPGTXUxx/Betterave-Sesame.jpg'); // INCORRECT: %2F got decoded but for compat reasons we cannot change this anymore... - assert.equal(uri1.query, 'alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'); - assert.equal(uri1.fragment, ''); + test('Nonce does not match on login #75755', function () { + let uri = URI.parse('http://localhost:60371/signin?nonce=iiK1zRI%2BHyDCKb2zatvrYA%3D%3D'); + assertComponents(uri, 'http', 'localhost:60371', '/signin', 'nonce=iiK1zRI+HyDCKb2zatvrYA==', ''); + assertToString(uri, 'http://localhost:60371/signin?nonce=iiK1zRI%2BHyDCKb2zatvrYA%3D%3D'); }); test('URI#parse creates normalized output', function () { @@ -664,5 +685,6 @@ suite('URI', () => { assertToString('http://foo@localhost/far'); assertToString('http://foo:bAr@localhost:8080/far'); assertToString('http://foo@localhost:8080/far'); + assertToString('http://localhost:60371/signin?nonce=iiK1zRI%2BHyDCKb2zatvrYA%3D%3D'); }); }); From ccbb0da040fff52c388fa2cea60aac2489cd6bcc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Oct 2019 11:08:47 +0200 Subject: [PATCH 82/89] env - move up logExtensionHostCommunication --- src/vs/platform/environment/common/environment.ts | 1 + src/vs/platform/environment/node/environmentService.ts | 2 ++ .../services/environment/browser/environmentService.ts | 3 +-- .../services/environment/common/environmentService.ts | 1 - .../environment/electron-browser/environmentService.ts | 3 --- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 46237677048..eaa3bc1bae1 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -142,6 +142,7 @@ export interface IEnvironmentService { extensionsPath?: string; extensionDevelopmentLocationURI?: URI[]; extensionTestsLocationURI?: URI; + logExtensionHostCommunication?: boolean; debugExtensionHost: IExtensionHostDebugParams; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index e51932e92be..cd86c866c06 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -235,6 +235,8 @@ export class EnvironmentService implements IEnvironmentService { @memoize get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); } + @memoize + get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } get isBuilt(): boolean { return !process.env['VSCODE_DEV']; } get verbose(): boolean { return !!this._args.verbose; } diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 417335c2a42..dcfd77b2425 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -230,6 +230,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment machineSettingsHome!: URI; machineSettingsResource!: URI; + userHome!: string; userDataPath!: string; appRoot!: string; appSettingsHome!: URI; @@ -245,8 +246,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment galleryMachineIdResource?: URI; - userHome!: string; - //#endregion constructor(readonly options: IBrowserWorkbenchEnvironmentConstructionOptions) { } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index a28cfc41e4e..4728f3e5102 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -20,7 +20,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly options?: IWorkbenchConstructionOptions; readonly logFile: URI; - readonly logExtensionHostCommunication: boolean; readonly webviewExternalEndpoint: string; readonly webviewResourceRoot: string; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index f6ee5070293..8c47e17bcc5 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -36,9 +36,6 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem @memoize get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.windowId}.log`)); } - @memoize - get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } - constructor( readonly configuration: IWindowConfiguration, execPath: string, From 81cf72357b1387bd94fece60c88178407c3efc72 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 24 Oct 2019 11:19:44 +0200 Subject: [PATCH 83/89] Skip the task quick pick when only one task + setting Related to https://github.com/microsoft/vscode/pull/47853 --- .../tasks/browser/abstractTaskService.ts | 24 ++++++++++++++++--- .../tasks/browser/task.contribution.ts | 5 ++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 4dbc9e5b8d1..65ad6b2ac93 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -83,6 +83,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; +const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; @@ -2022,6 +2023,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise { + const tokenSource = new CancellationTokenSource(); + const cancellationToken: CancellationToken = tokenSource.token; let _createEntries = (): Promise[]> => { if (Array.isArray(tasks)) { return Promise.resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); @@ -2029,15 +2032,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } }; - return this.quickInputService.pick(_createEntries().then((entries) => { - if ((entries.length === 0) && defaultEntry) { + const pickEntries = _createEntries().then((entries) => { + if ((entries.length === 1) && this.configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { + tokenSource.cancel(); + } else if ((entries.length === 0) && defaultEntry) { entries.push(defaultEntry); } else if (entries.length > 1 && additionalEntries && additionalEntries.length > 0) { entries.push({ type: 'separator', label: '' }); entries.push(additionalEntries[0]); } return entries; - }), { + }); + return this.quickInputService.pick(pickEntries, { placeHolder, matchOnDescription: true, onDidTriggerItemButton: context => { @@ -2049,6 +2055,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.openConfig(task); } } + }, cancellationToken).then(async (selection) => { + if (cancellationToken.isCancellationRequested) { + // canceled when there's only one task + const task = (await pickEntries)[0]; + if ((task).task) { + selection = task; + } + } + if (!selection) { + return; + } + return selection; }); } diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index bb322ab609c..9aa46f4d6b9 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -367,6 +367,11 @@ configurationRegistry.registerConfiguration({ markdownDescription: nls.localize('task.quickOpen.detail', "Controls whether to show the task detail for task that have a detail in the Run Task quick pick."), type: 'boolean', default: true + }, + 'task.quickOpen.skip': { + type: 'boolean', + description: nls.localize('task.quickOpen.skip', "Controls whether the task quick pick is skipped when there is only one task to pick from."), + default: false } } }); From a143fc36f8ee58db069c0bd2a14ec4ddeb29a009 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Oct 2019 11:16:24 +0200 Subject: [PATCH 84/89] fixes #83205 --- src/vs/code/electron-main/sharedProcess.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index d847e4ebcc5..fc716c82be1 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -6,13 +6,14 @@ import { assign } from 'vs/base/common/objects'; import { memoize } from 'vs/base/common/decorators'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { BrowserWindow, ipcMain } from 'electron'; +import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron'; import { ISharedProcess } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; export class SharedProcess implements ISharedProcess { @@ -53,7 +54,7 @@ export class SharedProcess implements ISharedProcess { this.window.loadURL(url); // Prevent the window from dying - const onClose = (e: Event) => { + const onClose = (e: ElectronEvent) => { this.logService.trace('SharedProcess#close prevented'); // We never allow to close the shared process unless we get explicitly disposed() @@ -97,7 +98,8 @@ export class SharedProcess implements ISharedProcess { }); return new Promise(c => { - ipcMain.once('handshake:hello', ({ sender }: { sender: any }) => { + const onHello = Event.once(Event.fromNodeEventEmitter(ipcMain, 'handshake:hello', ({ sender }: { sender: WebContents }) => sender)); + disposables.add(onHello(sender => { sender.send('handshake:hey there', { sharedIPCHandle: this.environmentService.sharedIPCHandle, args: this.environmentService.args, @@ -106,7 +108,7 @@ export class SharedProcess implements ISharedProcess { disposables.add(toDisposable(() => sender.send('handshake:goodbye'))); ipcMain.once('handshake:im ready', () => c(undefined)); - }); + })); }); } From 666ad41b9cf3a6f5312198b96de90f2f521feb3b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 11:39:26 +0200 Subject: [PATCH 85/89] add tests from old bugs that have been resolved "as designed" --- src/vs/base/common/uri.ts | 2 +- src/vs/base/test/node/uri.test.ts | 54 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index c122ad20127..e56858a7d7d 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -623,7 +623,7 @@ function percentEncode(str: string, mustEncode: (code: number) => boolean): stri const enum EncodePart { user, authority, path, query, fragment } -const _normalEncoder: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isFragmentPercentEncodeSet, isQueryPrecentEncodeSet]; +const _normalEncoder: { (code: number): boolean }[] = [isUserInfoPercentEncodeSet, isC0ControlPercentEncodeSet, isPathPercentEncodeSet, isQueryPrecentEncodeSet, isFragmentPercentEncodeSet]; const _minimalEncoder: { (code: number): boolean }[] = [isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, isHashOrQuestionMark, () => false]; const _driveLetterRegExp = /^(\/?[a-z])(:|%3a)/i; diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index de2bcd12348..736a1bbf6b1 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -507,6 +507,51 @@ suite('URI', () => { assertToString(uri, 'http://localhost:60371/signin?nonce=iiK1zRI%2BHyDCKb2zatvrYA%3D%3D'); }); + test('URI.parse() failes with `Cannot read property \'toLowerCase\' of undefined` #75344', function () { + try { + URI.parse('abc'); + assert.ok(false); + } catch (e) { + assert.ok(e instanceof Error && e.message.indexOf('[UriError]:')); + } + }); + + test('vscode.Uri.parse is double encoding certain characters', function () { + const inStr = 'https://github.com/PowerShell/vscode-powershell#reporting-problems'; + assertToString(inStr, inStr); + assertComponents(inStr, 'https', 'github.com', '/PowerShell/vscode-powershell', '', 'reporting-problems'); + }); + + test('Symbols in URL fragment should not be encoded #76635', function () { + const inStr = 'http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/CSharpCompilationOptions.cs,20'; + assertToString(inStr, inStr); + assertComponents(inStr, 'http', 'source.roslyn.io', '/', '', 'Microsoft.CodeAnalysis.CSharp/CSharpCompilationOptions.cs,20'); + }); + + test('vscode.env.openExternal is not working correctly because of unnecessary escape processing. #76606', function () { + const inStr = 'x-github-client://openRepo/https://github.com/wraith13/open-in-github-desktop-vscode.git'; + assertToString(inStr, 'x-github-client://openrepo/https://github.com/wraith13/open-in-github-desktop-vscode.git'); // lower-cased authory + assertComponents(inStr, 'x-github-client', 'openRepo', '/https://github.com/wraith13/open-in-github-desktop-vscode.git', '', ''); + }); + + test('When I click on a link in the terminal, browser opens with a URL which seems to be the link, but run through decodeURIComponent #52211', function () { + const inStr = 'http://localhost:8448/#/repository?path=%2Fhome%2Fcapaj%2Fgit_projects%2Fartillery'; + assertToString(inStr, inStr); + assertComponents(inStr, 'http', 'localhost:8448', '/', '', '/repository?path=/home/capaj/git_projects/artillery'); // INCORRECT %2F lost + }); + + test('Terminal breaks weblink for fish shell #44278', function () { + const inStr = 'https://eu-west-1.console.aws.amazon.com/cloudformation/home\\?region=eu-west-1#/stacks\\?filter=active'; + assertToString(inStr, inStr); + assertComponents(inStr, 'https', 'eu-west-1.console.aws.amazon.com', '/cloudformation/home\\', 'region=eu-west-1', '/stacks\\?filter=active'); + }); + + test('Markdown mode cannot open links that contains some codes by percent-encoding. #32026', function () { + const inStr = 'https://www.google.co.jp/search?q=%91%E5'; + assertToString(inStr, inStr); + assertComponents(inStr, 'https', 'www.google.co.jp', '/search', 'q=%91%E5', ''); + }); + test('URI#parse creates normalized output', function () { function assertToString(input: string, output: string = input): void { const uri = URI.parse(input); @@ -686,5 +731,14 @@ suite('URI', () => { assertToString('http://foo:bAr@localhost:8080/far'); assertToString('http://foo@localhost:8080/far'); assertToString('http://localhost:60371/signin?nonce=iiK1zRI%2BHyDCKb2zatvrYA%3D%3D'); + assertToString('https://github.com/PowerShell/vscode-powershell#reporting-problems'); + assertToString('http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/CSharpCompilationOptions.cs,20'); + // assertToString('x-github-client://openRepo/https://github.com/wraith13/open-in-github-desktop-vscode.git'); we lower-case + assertToString('x-github-client://openrepo/https://github.com/wraith13/open-in-github-desktop-vscode.git'); + assertToString('http://www.google.com/?parameter1=\'http://imageserver.domain.com/?parameter2=1\''); + assertToString('http://some.ws/page?id=123&select=%22quoted_string%22'); + // assertToString('https://eu-west-1.console.aws.amazon.com/cloudformation/home\\?region=eu-west-1#/stacks\\?filter=active'); URL makes slash out of backslash + assertToString('http://localhost/?user=test%2B1@example.com'); + assertToString('https://www.google.co.jp/search?q=%91%E5'); }); }); From 175ab4aa51cba05bd24acbf55eac87c52f4a3750 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 11:45:38 +0200 Subject: [PATCH 86/89] don't warn when fixing missing scheme, just do it... bring back strict mode for parse --- src/vs/base/common/uri.ts | 16 +++++++++++----- src/vs/base/test/node/uri.test.ts | 5 +++++ src/vs/monaco.d.ts | 4 +++- src/vs/vscode.d.ts | 4 ++-- .../api/common/extHostTypeConverters.ts | 2 +- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index e56858a7d7d..d00eb22908c 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -41,10 +41,14 @@ function _validateUri(ret: URI): void { } // graceful behaviour when scheme is missing: fallback to using 'file'-scheme -function _schemeFix(scheme: string): string { +function _schemeFix(scheme: string, strict?: boolean): string { if (!scheme) { - console.trace('BAD uri lacks scheme, falling back to file-scheme.'); - scheme = 'file'; + if (strict) { + throw new Error('[UriError]: A scheme must be provided'); + } else { + // console.trace('BAD uri lacks scheme, falling back to file-scheme.'); + scheme = 'file'; + } } return scheme; } @@ -263,15 +267,17 @@ export class URI implements UriComponents { * Creates a new URI from a string, e.g. `http://www.msft.com/some/path`, * `file:///usr/home`, or `scheme:with/path`. * + * *Note:* When the input lacks a scheme then `file` is used. + * * @param value A string which represents an URI (see `URI#toString`). */ - static parse(value: string): URI { + static parse(value: string, strict?: boolean): URI { const match = _uriRegExp.exec(value); if (!match) { throw new Error(`[UriError]: Invalid input: ${value}`); } - const scheme = _schemeFix(match[MatchIndex.scheme]) || ''; + const scheme = _schemeFix(match[MatchIndex.scheme], strict) || ''; const authority = match[MatchIndex.authority] || ''; const path = _referenceResolution(scheme, match[MatchIndex.path] || ''); const query = match[MatchIndex.query] || ''; diff --git a/src/vs/base/test/node/uri.test.ts b/src/vs/base/test/node/uri.test.ts index 736a1bbf6b1..b9fbd24de8c 100644 --- a/src/vs/base/test/node/uri.test.ts +++ b/src/vs/base/test/node/uri.test.ts @@ -235,6 +235,11 @@ suite('URI', () => { assert.throws(() => URI.parse('file:////shares/files/p.cs')); }); + test('URI#parse, missing scheme', () => { + assert.throws(() => URI.parse('/foo/bar', true)); + assertToString('/foo/bar', 'file:///foo/bar'); + }); + test('URI#file, win-speciale', () => { if (isWindows) { let value = URI.file('c:\\test\\drive'); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b2158819314..ed218495220 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -129,9 +129,11 @@ declare namespace monaco { * Creates a new Uri from a string, e.g. `http://www.msft.com/some/path`, * `file:///usr/home`, or `scheme:with/path`. * + * *Note:* When the input lacks a scheme then `file` is used. + * * @param value A string which represents an Uri (see `Uri#toString`). */ - static parse(value: string): Uri; + static parse(value: string, strict?: boolean): Uri; /** * Creates a new Uri from a file system path, e.g. `c:\my\files`, * `/usr/home`, or `\\server\share\some\path`. diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 27b2d1c77f4..e8c81c72b4b 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1236,8 +1236,8 @@ declare module 'vscode' { * `file:///usr/home`, or `scheme:with/path`. * * *Note* that for a while uris without a `scheme` were accepted. That is not correct - * as all uris should have a scheme. To avoid breakage of existing code the optional - * `strict`-argument has been added. We *strongly* advise to use it, e.g. `Uri.parse('my:uri', true)` + * as all uris should have a scheme. When missing the `file`-scheme is being used unless + * the `strict`-argument is `true` in which case an error is thrown. * * @see [Uri.toString](#Uri.toString) * @param value The string value of an Uri. diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c3f84a27286..9d0e711bfe8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -260,7 +260,7 @@ export namespace MarkdownString { const collectUri = (href: string): string => { try { - let uri = URI.parse(href); + let uri = URI.parse(href, true); uri = uri.with({ query: _uriMassage(uri.query, resUris) }); resUris[href] = uri; } catch (e) { From 92292f4fe4d2681ef980c3995e720af61f54ce37 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 11:51:56 +0200 Subject: [PATCH 87/89] remove encodeURI(uri.toString(true)) workarounds --- src/vs/editor/browser/services/openerService.ts | 2 +- src/vs/workbench/contrib/webview/common/portMapping.ts | 8 ++++---- src/vs/workbench/electron-browser/window.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 77b12e03583..2ea275f4515 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -149,7 +149,7 @@ export class OpenerService extends Disposable implements IOpenerService { private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise { const { resolved } = await this.resolveExternalUri(resource, options); - dom.windowOpenNoOpener(encodeURI(resolved.toString(true))); + dom.windowOpenNoOpener(resolved.toString()); return true; } diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/workbench/contrib/webview/common/portMapping.ts index 69c216631ba..79904ad749c 100644 --- a/src/vs/workbench/contrib/webview/common/portMapping.ts +++ b/src/vs/workbench/contrib/webview/common/portMapping.ts @@ -37,16 +37,16 @@ export class WebviewPortMappingManager extends Disposable { if (tunnel.tunnelLocalPort === mapping.webviewPort) { return undefined; } - return encodeURI(uri.with({ + return uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}`, - }).toString(true)); + }).toString(); } } if (mapping.webviewPort !== mapping.extensionHostPort) { - return encodeURI(uri.with({ + return uri.with({ authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}` - }).toString(true)); + }).toString(); } } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 23ea962fdf8..06ef5bbbc45 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -408,7 +408,7 @@ export class ElectronWindow extends Disposable { // the main process to prevent window focus issues. if (this.shouldOpenExternal(resource, options)) { const { resolved } = await this.openerService.resolveExternalUri(resource, options); - const success = await this.electronService.openExternal(encodeURI(resolved.toString(true))); + const success = await this.electronService.openExternal(resolved.toString()); if (!success && resolved.scheme === Schemas.file) { // if opening failed, and this is a file, we can still try to reveal it await this.electronService.showItemInFolder(resolved.fsPath); From 525f1e4754201c9f833de8ef131cfc87cb6d3f6f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 24 Oct 2019 11:52:24 +0200 Subject: [PATCH 88/89] Fix where user tasks show in the tasks quick pick --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 65ad6b2ac93..b55cfdd5b6d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1998,7 +1998,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (let task of tasks) { let key = task.getRecentlyUsedKey(); if (!key || !recentlyUsedTasks.has(key)) { - if (task._source.kind === TaskSourceKind.Workspace) { + if ((task._source.kind === TaskSourceKind.Workspace) || (task._source.kind === TaskSourceKind.User)) { configured.push(task); } else { detected.push(task); From 10fe086550eef76493c456f0ae2e83659fdb1c31 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Oct 2019 11:53:48 +0200 Subject: [PATCH 89/89] fix test failure after more strict parse in markdown convert --- .../test/electron-browser/api/extHostTypeConverter.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts index 63f13266677..c9d7dbe85b9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts @@ -22,13 +22,11 @@ suite('ExtHostTypeConverter', function () { data = MarkdownString.from('Hello [link](foo)'); assert.equal(data.value, 'Hello [link](foo)'); - assert.equal(size(data.uris!), 1); - assert.ok(!!data.uris!['foo']); + assert.equal(size(data.uris!), 0); data = MarkdownString.from('Hello [link](www.noscheme.bad)'); assert.equal(data.value, 'Hello [link](www.noscheme.bad)'); - assert.equal(size(data.uris!), 1); - assert.ok(!!data.uris!['www.noscheme.bad']); + assert.equal(size(data.uris!), 0); data = MarkdownString.from('Hello [link](foo:path)'); assert.equal(data.value, 'Hello [link](foo:path)');