diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml new file mode 100644 index 0000000000..ca26083b40 --- /dev/null +++ b/.github/workflows/english-please.yml @@ -0,0 +1,30 @@ +name: English Please +on: + issues: + types: [edited] + +# also make changes in ./on-label.yml and ./on-open.yml +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + if: contains(github.event.issue.labels.*.name, '*english-please') + uses: actions/checkout@v2 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v7 + path: ./actions + - name: Install Actions + if: contains(github.event.issue.labels.*.name, '*english-please') + run: npm install --production --prefix ./actions + - name: Run English Please + if: contains(github.event.issue.labels.*.name, '*english-please') + uses: ./actions/english-please + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + cognitiveServicesAPIKey: ${{secrets.AZURE_TEXT_TRANSLATOR_KEY}} + nonEnglishLabel: "*english-please" + needsMoreInfoLabel: "needs more info" + translatorRequestedLabelPrefix: "translation-required-" + translatorRequestedLabelColor: "c29cff" diff --git a/.vscode/launch.json b/.vscode/launch.json index 7b8ecd2604..27d8205ec0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,18 +14,26 @@ { "type": "node", "request": "attach", + "restart": true, "name": "Attach to Extension Host", + "timeout": 30000, "port": 5870, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "hidden": true + } }, { - "type": "chrome", + "type": "pwa-chrome", "request": "attach", "name": "Attach to Shared Process", "port": 9222, - "urlFilter": "*" + "urlFilter": "*sharedProcess.html*", + "presentation": { + "hidden": true + } }, { "type": "node", @@ -67,9 +75,10 @@ "port": 9222 }, { - "type": "chrome", + "type": "pwa-chrome", "request": "launch", "name": "Launch azuredatastudio", + "browserLaunchLocation": "workspace", "windows": { "runtimeExecutable": "${workspaceFolder}/scripts/sql.bat" }, @@ -97,25 +106,7 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], - }, - { - "type": "node", - "request": "launch", - "name": "Launch ADS (Main Process)", - "runtimeExecutable": "${workspaceFolder}/scripts/sql.sh", - "windows": { - "runtimeExecutable": "${workspaceFolder}/scripts/sql.bat", - }, - "runtimeArgs": [ - "--no-cached-data" - ], - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ], - "presentation": { - "group": "2_launch", - "order": 1 - } + "browserLaunchLocation": "workspace" }, { "type": "chrome", @@ -150,7 +141,7 @@ "web" ], "presentation": { - "group": "2_launch", + "group": "0_vscode", "order": 2 } }, @@ -161,7 +152,7 @@ "url": "http://localhost:8080", "preLaunchTask": "Run web", "presentation": { - "group": "2_launch", + "group": "0_vscode", "order": 3 } }, @@ -270,13 +261,15 @@ ] }, { - "name": "Debug azuredatastudio Main and Renderer", + "name": "Azure Data Studio", "configurations": [ "Launch azuredatastudio", - "Attach to Main Process" + "Attach to Main Process", + "Attach to Extension Host", + "Attach to Shared Process", ], "presentation": { - "group": "1_vscode", + "group": "0_vscode", "order": 1 } }, diff --git a/.vscode/searches/es6.code-search b/.vscode/searches/es6.code-search index df7088e5a1..850f011473 100644 --- a/.vscode/searches/es6.code-search +++ b/.vscode/searches/es6.code-search @@ -2,7 +2,7 @@ # Flags: CaseSensitive WordMatch # ContextLines: 2 -9 results - 4 files +10 results - 4 files src/vs/base/common/arrays.ts: 401 @@ -24,17 +24,17 @@ src/vs/base/common/arrays.ts: 564 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { src/vs/base/common/map.ts: - 9 - 10 /** - 11: * @deprecated ES6: use `[...SetOrMap.values()]` - 12 */ - 13 export function values(set: Set): V[]; + 11 + 12 /** + 13: * @deprecated ES6: use `[...SetOrMap.values()]` + 14 */ + 15 export function values(set: Set): V[]; - 20 - 21 /** - 22: * @deprecated ES6: use `[...map.keys()]` - 23 */ - 24 export function keys(map: Map): K[] { + 22 + 23 /** + 24: * @deprecated ES6: use `[...map.keys()]` + 25 */ + 26 export function keys(map: Map): K[] { src/vs/base/common/objects.ts: 115 @@ -61,3 +61,9 @@ src/vs/base/common/strings.ts: 169: * @deprecated ES6: use `String.endsWith` 170 */ 171 export function endsWith(haystack: string, needle: string): boolean { + + 853 + 854 /** + 855: * @deprecated ES6 + 856 */ + 857 export function repeat(s: string, count: number): string { diff --git a/.vscode/settings.json b/.vscode/settings.json index 2a9a8093ce..6aadf86f7e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,5 +72,6 @@ "files.insertFinalNewline": true, "[typescript]": { "editor.defaultFormatter": "vscode.typescript-language-features" - } + }, + "typescript.tsc.autoDetect": "off" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index eafc311235..b57b80fd84 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,7 +3,7 @@ "tasks": [ { "type": "npm", - "script": "watch", + "script": "watchd", "label": "Build VS Code", "group": { "kind": "build", @@ -45,6 +45,16 @@ "applyTo": "allDocuments" } }, + { + "type": "npm", + "script": "kill-watchd", + "label": "Kill Build VS Code", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" + }, { "label": "Run tests", "type": "shell", diff --git a/.yarnrc b/.yarnrc index 7808166004..c3c11fbeb9 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "7.1.11" +target "7.2.2" runtime "electron" diff --git a/build/.nativeignore b/build/.nativeignore index 3d6e939e2e..9f7967cecf 100644 --- a/build/.nativeignore +++ b/build/.nativeignore @@ -107,13 +107,13 @@ kerberos/build/** # END SQL Modules -nsfw/binding.gyp -nsfw/build/** -nsfw/src/** -nsfw/openpa/** -nsfw/includes/** -!nsfw/build/Release/*.node -!nsfw/**/*.a +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a vsda/build/** vsda/ci/** diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 9ee7e7813f..139b4c9d74 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -44,10 +44,10 @@ steps: - script: | ./scripts/test.sh --tfs "Unit Tests" displayName: Run Unit Tests (Electron) -# - script: | {{SQL CARBON EDIT}} disable for now @TODO @anthonydresser -# yarn test-browser --browser chromium --browser webkit +# - script: | {{SQL CARBON EDIT}} disable +# yarn test-browser --browser chromium --browser webkit --browser firefox # displayName: Run Unit Tests (Browser) -# - script: | {{SQL CARBON EDIT}} remove step +# - script: | {{SQL CARBON EDIT}} disable # ./scripts/test-integration.sh --tfs "Integration Tests" # displayName: Run Integration Tests (Electron) - task: PublishTestResults@2 diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 4ed93a41f4..e354e28dc3 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -77,23 +77,6 @@ steps: yarn postinstall displayName: Run postinstall scripts condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - STABLE_GITHUB_ID: "baa8a44b5e861d918709" - STABLE_GITHUB_SECRET: $(stable-github-client-secret) - EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" - EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) - VSO_GITHUB_ID: "3d4be8f37a0325b5817d" - VSO_GITHUB_SECRET: $(vso-github-client-secret) - VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" - VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) - VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" - VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) - GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" - GITHUB_APP_SECRET: $(github-app-client-secret) - script: | set -e @@ -118,7 +101,7 @@ steps: - script: | set -e - yarn test-browser --build --browser chromium --browser webkit + yarn test-browser --build --browser chromium --browser webkit --browser firefox displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index b91b01138d..e0b1ef7d61 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,8 +1,12 @@ pool: vmImage: 'Ubuntu-16.04' -trigger: none -pr: none +trigger: + branches: + include: ['master'] +pr: + branches: + include: ['master'] steps: - task: NodeTool@0 @@ -27,10 +31,10 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git checkout origin/electron-6.0.x + git checkout origin/electron-8.0.x git merge origin/master # Push master branch into exploration branch - git push origin HEAD:electron-6.0.x + git push origin HEAD:electron-8.0.x displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 2494196aae..a33d68bed7 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -52,10 +52,10 @@ steps: - script: | DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" displayName: Run Unit Tests (Electron) -# - script: | {{SQL CARBON EDIT}} disable for now @TODO @anthonydresser +# - script: | {{SQL CARBON EDIT}} disable # DISPLAY=:10 yarn test-browser --browser chromium # displayName: Run Unit Tests (Browser) -# - script: | {{SQL CARBON EDIT}} remove step +# - script: | {{SQL CARBON EDIT}} disable # DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" # displayName: Run Integration Tests (Electron) - task: PublishTestResults@2 diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml index 066e42af3d..68ae4ee8b6 100644 --- a/build/azure-pipelines/linux/product-build-linux-multiarch.yml +++ b/build/azure-pipelines/linux/product-build-linux-multiarch.yml @@ -86,23 +86,6 @@ steps: yarn postinstall displayName: Run postinstall scripts condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - STABLE_GITHUB_ID: "baa8a44b5e861d918709" - STABLE_GITHUB_SECRET: $(stable-github-client-secret) - EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" - EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) - VSO_GITHUB_ID: "3d4be8f37a0325b5817d" - VSO_GITHUB_SECRET: $(vso-github-client-secret) - VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" - VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) - VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" - VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) - GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" - GITHUB_APP_SECRET: $(github-app-client-secret) - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 119f80cd92..cbe3bf051e 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -76,23 +76,6 @@ steps: yarn postinstall displayName: Run postinstall scripts condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - STABLE_GITHUB_ID: "baa8a44b5e861d918709" - STABLE_GITHUB_SECRET: $(stable-github-client-secret) - EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" - EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) - VSO_GITHUB_ID: "3d4be8f37a0325b5817d" - VSO_GITHUB_SECRET: $(vso-github-client-secret) - VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" - VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) - VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" - VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) - GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" - GITHUB_APP_SECRET: $(github-app-client-secret) - script: | set -e diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 6c28724824..1f665c8b3d 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -74,9 +74,9 @@ steps: - script: | set -e - yarn postinstall - displayName: Run postinstall scripts - condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + yarn generate-github-config + displayName: Generate GitHub config + condition: succeeded() env: OSS_GITHUB_ID: "a5d3c261b032765a78de" OSS_GITHUB_SECRET: $(oss-github-client-secret) @@ -95,6 +95,12 @@ steps: GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" GITHUB_APP_SECRET: $(github-app-client-secret) +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + # Mixin must run before optimize, because the CSS loader will # inline small SVGs - script: | diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 1d4ab83e1a..b73cd04a96 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -63,7 +63,7 @@ steps: MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." - MESSAGE2="[@octref, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." + MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ -H 'Content-type: application/json; charset=utf-8' \ diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 680fb08ad3..202ea26287 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -49,10 +49,10 @@ steps: - powershell: | .\scripts\test.bat --tfs "Unit Tests" displayName: Run Unit Tests (Electron) -# - powershell: | {{SQL CARBON EDIT}} disable for now @TODO @anthonydresser -# yarn test-browser --browser chromium +# - powershell: | {{SQL CARBON EDIT}} disable +# yarn test-browser --browser chromium --browser firefox # displayName: Run Unit Tests (Browser) -# - powershell: | {{SQL CARBON EDIT}} remove step +# - powershell: | {{SQL CARBON EDIT}} disable # .\scripts\test-integration.bat --tfs "Integration Tests" # displayName: Run Integration Tests (Electron) - task: PublishTestResults@2 diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 20699bf922..c428642454 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -86,23 +86,6 @@ steps: exec { yarn postinstall } displayName: Run postinstall scripts condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - STABLE_GITHUB_ID: "baa8a44b5e861d918709" - STABLE_GITHUB_SECRET: $(stable-github-client-secret) - EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" - EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) - VSO_GITHUB_ID: "3d4be8f37a0325b5817d" - VSO_GITHUB_SECRET: $(vso-github-client-secret) - VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" - VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) - VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" - VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) - GITHUB_APP_ID: "Iv1.ae51e546bef24ff1" - GITHUB_APP_SECRET: $(github-app-client-secret) - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -132,7 +115,7 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { yarn test-browser --build --browser chromium } + exec { yarn test-browser --build --browser chromium --browser firefox } displayName: Run unit tests (Browser) condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 948627332f..a1297d1802 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -43,7 +43,7 @@ let editorEntryPoints = [ ]; let editorResources = [ - 'out-editor-build/vs/base/browser/ui/codiconLabel/**/*.ttf' + 'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf' ]; let BUNDLED_FILE_HEADER = [ diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 5c96a4a4b2..f83c7dd514 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -76,7 +76,7 @@ const vscodeResources = [ 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', - 'out-build/vs/base/browser/ui/codiconLabel/codicon/**', + 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', @@ -186,6 +186,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const checksums = computeChecksums(out, [ 'vs/workbench/workbench.desktop.main.js', 'vs/workbench/workbench.desktop.main.css', + 'vs/workbench/services/extensions/node/extensionHostProcess.js', 'vs/code/electron-browser/workbench/workbench.html', 'vs/code/electron-browser/workbench/workbench.js' ]); @@ -357,6 +358,7 @@ const buildRoot = path.dirname(root); const BUILD_TARGETS = [ { platform: 'win32', arch: 'ia32' }, { platform: 'win32', arch: 'x64' }, + { platform: 'win32', arch: 'arm64' }, { platform: 'darwin', arch: null, opts: { stats: true } }, { platform: 'linux', arch: 'ia32' }, { platform: 'linux', arch: 'x64' }, @@ -488,20 +490,30 @@ const generateVSCodeConfigurationTask = task.define('generate-vscode-configurati const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app'; const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`); - const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`); + const codeProc = cp.exec( + `${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`, + (err, stdout, stderr) => { + clearTimeout(timer); + if (err) { + console.log(`err: ${err} ${err.message} ${err.toString()}`); + reject(err); + } + if (stdout) { + console.log(`stdout: ${stdout}`); + } + + if (stderr) { + console.log(`stderr: ${stderr}`); + } + + resolve(); + } + ); const timer = setTimeout(() => { codeProc.kill(); reject(new Error('export-default-configuration process timed out')); - }, 10 * 1000); - - codeProc.stdout.on('data', d => console.log(d.toString())); - codeProc.stderr.on('data', d => console.log(d.toString())); - - codeProc.on('exit', () => { - clearTimeout(timer); - resolve(); - }); + }, 12 * 1000); codeProc.on('error', err => { clearTimeout(timer); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index e20374ec57..4407f02985 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -128,6 +128,7 @@ function archiveWin32Setup(arch) { gulp.task(task.define('vscode-win32-ia32-archive', task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32')))); gulp.task(task.define('vscode-win32-x64-archive', task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64')))); +gulp.task(task.define('vscode-win32-arm64-archive', task.series(util.rimraf(zipDir('arm64')), archiveWin32Setup('arm64')))); function copyInnoUpdater(arch) { return () => { @@ -150,3 +151,4 @@ gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdat gulp.task(task.define('vscode-win32-ia32-code-helper', task.series(updateIcon(path.join(buildPath('ia32'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); gulp.task(task.define('vscode-win32-x64-code-helper', task.series(updateIcon(path.join(buildPath('x64'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); +gulp.task(task.define('vscode-win32-arm64-code-helper', task.series(updateIcon(path.join(buildPath('arm64'), 'resources', 'app', 'out', 'vs', 'platform', 'files', 'node', 'watcher', 'win32', 'CodeHelper.exe'))))); diff --git a/build/lib/eslint/vscode-dts-event-naming.js b/build/lib/eslint/vscode-dts-event-naming.js index 0b399e9539..d8c64aff81 100644 --- a/build/lib/eslint/vscode-dts-event-naming.js +++ b/build/lib/eslint/vscode-dts-event-naming.js @@ -27,13 +27,7 @@ module.exports = new (_a = class ApiEventNaming { ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node) => { var _a, _b; const def = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent; - let ident; - if ((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.Identifier) { - ident = def; - } - else if (((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || (def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { - ident = def.key; - } + const ident = this.getIdent(def); if (!ident) { // event on unknown structure... return context.report({ @@ -76,6 +70,18 @@ module.exports = new (_a = class ApiEventNaming { } }; } + getIdent(def) { + if (!def) { + return; + } + if (def.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + return def; + } + else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + return def.key; + } + return this.getIdent(def.parent); + } }, _a._nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/, _a); diff --git a/build/lib/eslint/vscode-dts-event-naming.ts b/build/lib/eslint/vscode-dts-event-naming.ts index 6e23562628..28706de010 100644 --- a/build/lib/eslint/vscode-dts-event-naming.ts +++ b/build/lib/eslint/vscode-dts-event-naming.ts @@ -32,14 +32,7 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node: any) => { const def = (node).parent?.parent?.parent; - let ident: TSESTree.Identifier | undefined; - - if (def?.type === AST_NODE_TYPES.Identifier) { - ident = def; - - } else if ((def?.type === AST_NODE_TYPES.TSPropertySignature || def?.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) { - ident = def.key; - } + const ident = this.getIdent(def); if (!ident) { // event on unknown structure... @@ -87,5 +80,19 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { } }; } + + private getIdent(def: TSESTree.Node | undefined): TSESTree.Identifier | undefined { + if (!def) { + return; + } + + if (def.type === AST_NODE_TYPES.Identifier) { + return def; + } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) { + return def.key; + } + + return this.getIdent(def.parent); + } }; diff --git a/build/package.json b/build/package.json index 917d7fe5a8..b65f7654a1 100644 --- a/build/package.json +++ b/build/package.json @@ -48,7 +48,7 @@ "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", "terser": "4.3.8", - "typescript": "^3.9.0-dev.20200327", + "typescript": "^3.9.0-dev.20200420", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.5.4", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index 8ae54996d8..1605a3f7d0 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -3462,10 +3462,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^3.9.0-dev.20200327: - version "3.9.0-dev.20200327" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200327.tgz#52179aae816587f772a0526e91143760f2bee42f" - integrity sha512-/TWD/zPvhAcN2Toqx2NBQ+oDVGVj4iqupjWcUAwL45TfcODeHpzszneABR1b/EjHbtUObtLH40vy5Z6rdVvKzg== +typescript@^3.9.0-dev.20200420: + version "3.9.0-dev.20200420" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200420.tgz#99c2bc0936dbf4479b0b5260d80475ed494b1532" + integrity sha512-36MW6V+oXNnsSgliSjUWvtOkO21g9+iFGHPFv+O06HsCl3dcuqzBac17m8xuOuWo1LUlEgS6yAnD9fiVgvmCfg== typical@^4.0.0: version "4.0.0" diff --git a/cgmanifest.json b/cgmanifest.json index 40d6e42aa7..2d5e8a2d66 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "d17dfabfcba7bd0bc994b8dac5f5d2000bef572c" + "commitHash": "959e80cc53cbebf8eb1d62eb2d14fa8fd86b0394" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.1.11" + "version": "7.2.2" }, { "component": { diff --git a/extensions/bat/package.json b/extensions/bat/package.json index 00bd84e4ae..dc543aef40 100644 --- a/extensions/bat/package.json +++ b/extensions/bat/package.json @@ -23,7 +23,7 @@ }], "snippets": [{ "language": "bat", - "path": "./snippets/batchfile.snippets.json" + "path": "./snippets/batchfile.code-snippets" }] } -} \ No newline at end of file +} diff --git a/extensions/bat/snippets/batchfile.snippets.json b/extensions/bat/snippets/batchfile.code-snippets similarity index 100% rename from extensions/bat/snippets/batchfile.snippets.json rename to extensions/bat/snippets/batchfile.code-snippets diff --git a/extensions/git/package.json b/extensions/git/package.json index 9ff99819cd..a29fc3db06 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -411,6 +411,26 @@ "category": "Git" } ], + "keybindings": [ + { + "command": "git.stageSelectedRanges", + "key": "ctrl+k ctrl+alt+s", + "mac": "cmd+k cmd+alt+s", + "when": "isInDiffEditor" + }, + { + "command": "git.unstageSelectedRanges", + "key": "ctrl+k ctrl+u", + "mac": "cmd+k cmd+u", + "when": "isInDiffEditor" + }, + { + "command": "git.revertSelectedRanges", + "key": "ctrl+k ctrl+r", + "mac": "cmd+k cmd+r", + "when": "isInDiffEditor" + } + ], "menus": { "commandPalette": [ { @@ -1643,6 +1663,12 @@ "default": "mixed", "description": "%config.untrackedChanges%", "scope": "resource" + }, + "git.showCommitInput": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.showCommitInput%" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index c9d641e346..d376e32dd5 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -102,7 +102,7 @@ "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.", "config.smartCommitChanges.all": "Automatically stage all changes.", - "config.smartCommitChanges.tracked": "Automatically staged tracked changes only.", + "config.smartCommitChanges.tracked": "Automatically stage tracked changes only.", "config.suggestSmartCommit": "Suggests to enable smart commit (commit all changes when there are no staged changes).", "config.enableCommitSigning": "Enables commit signing with GPG.", "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", @@ -143,6 +143,7 @@ "config.untrackedChanges.mixed": "All changes, tracked and untracked, appear together and behave equally.", "config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.", "config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.", + "config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.", "colors.added": "Color for added resources.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 03318f6481..177d62889f 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,8 +5,8 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions } from './git'; -import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, GitExtension, RefType, RemoteSourceProvider } from './git'; +import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode'; import { mapEvent } from '../util'; import { toGitUri } from '../uri'; @@ -248,5 +248,82 @@ export class ApiImpl implements API { return result ? new ApiRepository(result) : null; } + registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable { + return this._model.registerRemoteSourceProvider(provider); + } + constructor(private _model: Model) { } } + +function getRefType(type: RefType): string { + switch (type) { + case RefType.Head: return 'Head'; + case RefType.RemoteHead: return 'RemoteHead'; + case RefType.Tag: return 'Tag'; + } + + return 'unknown'; +} + +function getStatus(status: Status): string { + switch (status) { + case Status.INDEX_MODIFIED: return 'INDEX_MODIFIED'; + case Status.INDEX_ADDED: return 'INDEX_ADDED'; + case Status.INDEX_DELETED: return 'INDEX_DELETED'; + case Status.INDEX_RENAMED: return 'INDEX_RENAMED'; + case Status.INDEX_COPIED: return 'INDEX_COPIED'; + case Status.MODIFIED: return 'MODIFIED'; + case Status.DELETED: return 'DELETED'; + case Status.UNTRACKED: return 'UNTRACKED'; + case Status.IGNORED: return 'IGNORED'; + case Status.INTENT_TO_ADD: return 'INTENT_TO_ADD'; + case Status.ADDED_BY_US: return 'ADDED_BY_US'; + case Status.ADDED_BY_THEM: return 'ADDED_BY_THEM'; + case Status.DELETED_BY_US: return 'DELETED_BY_US'; + case Status.DELETED_BY_THEM: return 'DELETED_BY_THEM'; + case Status.BOTH_ADDED: return 'BOTH_ADDED'; + case Status.BOTH_DELETED: return 'BOTH_DELETED'; + case Status.BOTH_MODIFIED: return 'BOTH_MODIFIED'; + } + + return 'UNKNOWN'; +} + +export function registerAPICommands(extension: GitExtension): Disposable { + return Disposable.from( + commands.registerCommand('git.api.getRepositories', () => { + const api = extension.getAPI(1); + return api.repositories.map(r => r.rootUri.toString()); + }), + + commands.registerCommand('git.api.getRepositoryState', (uri: string) => { + const api = extension.getAPI(1); + const repository = api.getRepository(Uri.parse(uri)); + + if (!repository) { + return null; + } + + const state = repository.state; + + const ref = (ref: Ref | undefined) => (ref && { ...ref, type: getRefType(ref.type) }); + const change = (change: Change) => ({ + uri: change.uri.toString(), + originalUri: change.originalUri.toString(), + renameUri: change.renameUri?.toString(), + status: getStatus(change.status) + }); + + return { + HEAD: ref(state.HEAD), + refs: state.refs.map(ref), + remotes: state.remotes, + submodules: state.submodules, + rebaseCommit: state.rebaseCommit, + mergeChanges: state.mergeChanges.map(change), + indexChanges: state.indexChanges.map(change), + workingTreeChanges: state.workingTreeChanges.map(change) + }; + }) + ); +} diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index fc0a057925..8868493e0a 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, SourceControlInputBox, Event, CancellationToken } from 'vscode'; +import { Uri, Event, Disposable, ProviderResult } from 'vscode'; +export { ProviderResult } from 'vscode'; export interface Git { readonly path: string; @@ -189,6 +190,19 @@ export interface Repository { commit(message: string, opts?: CommitOptions): Promise; } +export interface RemoteSource { + readonly name: string; + readonly description?: string; + readonly url: string | string[]; +} + +export interface RemoteSourceProvider { + readonly name: string; + readonly icon?: string; // codicon name + readonly supportsQuery?: boolean; + getRemoteSources(query?: string): ProviderResult; +} + export type APIState = 'uninitialized' | 'initialized'; export interface API { @@ -201,6 +215,7 @@ export interface API { toGitUri(uri: Uri, ref: string): Uri; getRepository(uri: Uri): Repository | null; + registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; } export interface GitExtension { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index feb24055a6..7b93854bab 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,10 +6,10 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; -import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git'; +import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git'; import { ForcePushMode, Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; @@ -18,6 +18,7 @@ import { fromGitUri, toGitUri, isGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; import { Log, LogLevel } from './log'; import { GitTimelineItem } from './timelineProvider'; +import { throttle, debounce } from './decorators'; const localize = nls.loadMessageBundle(); @@ -233,6 +234,71 @@ interface PushOptions { silent?: boolean; } +async function getQuickPickResult(quickpick: QuickPick): Promise { + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); + + quickpick.hide(); + return result; +} + +class RemoteSourceProviderQuickPick { + + private quickpick: QuickPick; + + constructor(private provider: RemoteSourceProvider) { + this.quickpick = window.createQuickPick(); + this.quickpick.ignoreFocusOut = true; + + if (provider.supportsQuery) { + this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); + this.quickpick.onDidChangeValue(this.onDidChangeValue, this); + } else { + this.quickpick.placeholder = localize('type to filter', "Repository name"); + } + } + + @debounce(300) + onDidChangeValue(): void { + this.query(); + } + + @throttle + async query(): Promise { + this.quickpick.busy = true; + + try { + const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + + if (remoteSources.length === 0) { + this.quickpick.items = [{ + label: localize('none found', "No remote repositories found."), + alwaysShow: true + }]; + } else { + this.quickpick.items = remoteSources.map(remoteSource => ({ + label: remoteSource.name, + description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), + remoteSource + })); + } + } catch (err) { + this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + } finally { + this.quickpick.busy = false; + } + } + + async pick(): Promise { + this.query(); + const result = await getQuickPickResult(this.quickpick); + return result?.remoteSource; + } +} + export class CommandCenter { private disposables: Disposable[]; @@ -290,7 +356,7 @@ export class CommandCenter { } @command('git.openResource') - async openResource(resource: Resource): Promise { + async openResource(resource: Resource, preserveFocus: boolean): Promise { const repository = this.model.getRepository(resource.resourceUri); if (!repository) { @@ -301,7 +367,7 @@ export class CommandCenter { const openDiffOnClick = config.get('openDiffOnClick'); if (openDiffOnClick) { - await this._openResource(resource, undefined, true, false); + await this._openResource(resource, undefined, preserveFocus, false); } else { await this.openFile(resource); } @@ -454,10 +520,51 @@ export class CommandCenter { @command('git.clone') async clone(url?: string, parentPath?: string): Promise { if (!url) { - url = await window.showInputBox({ - prompt: localize('repourl', "Repository URL"), - ignoreFocusOut: true - }); + const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); + quickpick.ignoreFocusOut = true; + + const providers = this.model.getRemoteProviders() + .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('clonefrom', "Clone from {1}", provider.name), alwaysShow: true, provider })); + + quickpick.placeholder = providers.length === 0 + ? localize('provide url', "Provide repository URL.") + : localize('provide url or pick', "Provide repository URL or pick a repository source."); + + const updatePicks = (value?: string) => { + if (value) { + quickpick.items = [{ + label: localize('repourl', "Clone from URL"), + description: value, + alwaysShow: true, + url: value + }, + ...providers]; + } else { + quickpick.items = providers; + } + }; + + quickpick.onDidChangeValue(updatePicks); + updatePicks(); + + const result = await getQuickPickResult(quickpick); + + if (result) { + if (result.url) { + url = result.url; + } else if (result.provider) { + const quickpick = new RemoteSourceProviderQuickPick(result.provider); + const remote = await quickpick.pick(); + + if (remote) { + if (typeof remote.url === 'string') { + url = remote.url; + } else if (remote.url.length > 0) { + url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + } + } } if (!url) { diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts deleted file mode 100644 index 1f394f9cd9..0000000000 --- a/extensions/git/src/contentProvider.ts +++ /dev/null @@ -1,150 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode'; -import { debounce, throttle } from './decorators'; -import { fromGitUri, toGitUri } from './uri'; -import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; -import { filterEvent, eventToPromise, isDescendant, pathEquals } from './util'; - -interface CacheRow { - uri: Uri; - timestamp: number; -} - -interface Cache { - [uri: string]: CacheRow; -} - -const THREE_MINUTES = 1000 * 60 * 3; -const FIVE_MINUTES = 1000 * 60 * 5; - -export class GitContentProvider { - - private _onDidChange = new EventEmitter(); - get onDidChange(): Event { return this._onDidChange.event; } - - private changedRepositoryRoots = new Set(); - private cache: Cache = Object.create(null); - private disposables: Disposable[] = []; - - constructor(private model: Model) { - this.disposables.push( - model.onDidChangeRepository(this.onDidChangeRepository, this), - model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), - workspace.registerTextDocumentContentProvider('git', this) - ); - - setInterval(() => this.cleanup(), FIVE_MINUTES); - } - - private onDidChangeRepository({ repository }: ModelChangeEvent): void { - this.changedRepositoryRoots.add(repository.root); - this.eventuallyFireChangeEvents(); - } - - private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void { - if (uri.scheme !== 'file') { - return; - } - - this._onDidChange.fire(toGitUri(uri, '', { replaceFileExtension: true })); - } - - @debounce(1100) - private eventuallyFireChangeEvents(): void { - this.fireChangeEvents(); - } - - @throttle - private async fireChangeEvents(): Promise { - if (!window.state.focused) { - const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused); - await eventToPromise(onDidFocusWindow); - } - - Object.keys(this.cache).forEach(key => { - const uri = this.cache[key].uri; - const fsPath = uri.fsPath; - - for (const root of this.changedRepositoryRoots) { - if (isDescendant(root, fsPath)) { - this._onDidChange.fire(uri); - return; - } - } - }); - - this.changedRepositoryRoots.clear(); - } - - async provideTextDocumentContent(uri: Uri): Promise { - let { path, ref, submoduleOf } = fromGitUri(uri); - - if (submoduleOf) { - const repository = this.model.getRepository(submoduleOf); - - if (!repository) { - return ''; - } - - if (ref === 'index') { - return await repository.diffIndexWithHEAD(path); - } else { - return await repository.diffWithHEAD(path); - } - } - - const repository = this.model.getRepository(uri); - - if (!repository) { - return ''; - } - - const cacheKey = uri.toString(); - const timestamp = new Date().getTime(); - const cacheValue: CacheRow = { uri, timestamp }; - - this.cache[cacheKey] = cacheValue; - - if (ref === '~') { - const fileUri = Uri.file(path); - const uriString = fileUri.toString(); - const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); - ref = indexStatus ? '' : 'HEAD'; - } else if (/^~\d$/.test(ref)) { - ref = `:${ref[1]}`; - } - - try { - return await repository.show(ref, path); - } catch (err) { - return ''; - } - } - - private cleanup(): void { - const now = new Date().getTime(); - const cache = Object.create(null); - - Object.keys(this.cache).forEach(key => { - const row = this.cache[key]; - const { path } = fromGitUri(row.uri); - const isOpen = workspace.textDocuments - .filter(d => d.uri.scheme === 'file') - .some(d => pathEquals(d.uri.fsPath, path)); - - if (isOpen || now - row.timestamp < THREE_MINUTES) { - cache[row.uri.toString()] = row; - } - }); - - this.cache = cache; - } - - dispose(): void { - this.disposables.forEach(d => d.dispose()); - } -} diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 53266f95df..265dae3375 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -443,7 +443,10 @@ export class Git { ); if (networkPath !== undefined) { return path.normalize( - repoUri.fsPath.replace(networkPath, `${letter.toLowerCase()}:`), + repoUri.fsPath.replace( + networkPath, + `${letter.toLowerCase()}:${networkPath.endsWith('\\') ? '\\' : ''}` + ), ); } } catch { } @@ -537,7 +540,8 @@ export class Git { options.env = assign({}, process.env, this.env, options.env || {}, { VSCODE_GIT_COMMAND: args[0], LC_ALL: 'en_US.UTF-8', - LANG: 'en_US.UTF-8' + LANG: 'en_US.UTF-8', + GIT_PAGER: 'cat' }); if (options.cwd) { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 04748bbbb5..269ccdf9dc 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -10,7 +10,6 @@ import { ExtensionContext, workspace, window, Disposable, commands, OutputChanne import { findGit, Git, IGit } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; -import { GitContentProvider } from './contentProvider'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; @@ -23,6 +22,7 @@ import { GitExtensionImpl } from './api/extension'; // import * as fs from 'fs'; import { createIPCServer, IIPCServer } from './ipc/ipcServer'; import { GitTimelineProvider } from './timelineProvider'; +import { registerAPICommands } from './api/api1'; const deactivateTasks: { (): Promise; }[] = []; @@ -80,7 +80,6 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann disposables.push( new CommandCenter(git, model, outputChannel, telemetryReporter), - new GitContentProvider(model), new GitFileSystemProvider(model), new GitDecorations(model), new GitProtocolHandler(), @@ -141,7 +140,7 @@ async function warnAboutMissingGit(): Promise { } }*/ -export async function activate(context: ExtensionContext): Promise { +export async function _activate(context: ExtensionContext): Promise { const disposables: Disposable[] = []; context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); @@ -183,10 +182,19 @@ export async function activate(context: ExtensionContext): Promise } } -// {{SQL CARBON EDIT}} - Rename info to _info to prevent error due to unused variable -async function checkGitVersion(_info: IGit): Promise { +export async function activate(context: ExtensionContext): Promise { + const result = await _activate(context); + context.subscriptions.push(registerAPICommands(result)); + return result; +} + +async function checkGitVersion(_info: IGit): Promise { // {{SQL CARBON EDIT}} - Rename info to _info to prevent error due to unused variable return; /* {{SQL CARBON EDIT}} return immediately + /*const config = workspace.getConfiguration('git'); + const shouldIgnore = config.get('ignoreLegacyWarning') === true; + + const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLegacyWarning') === true; diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 560c4a5d01..9b0958fe26 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -6,13 +6,13 @@ import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel } from 'vscode'; import { Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; -import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals } from './util'; +import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals, toDisposable } from './util'; import { Git } from './git'; import * as path from 'path'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; import { fromGitUri } from './uri'; -import { GitErrorCodes, APIState as State } from './api/git'; +import { GitErrorCodes, APIState as State, RemoteSourceProvider } from './api/git'; const localize = nls.loadMessageBundle(); @@ -74,6 +74,8 @@ export class Model { this._onDidChangeState.fire(state); } + private remoteProviders = new Set(); + private disposables: Disposable[] = []; constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) { @@ -447,6 +449,15 @@ export class Model { return undefined; } + registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable { + this.remoteProviders.add(provider); + return toDisposable(() => this.remoteProviders.delete(provider)); + } + + getRemoteProviders(): RemoteSourceProvider[] { + return [...this.remoteProviders.values()]; + } + dispose(): void { const openRepositories = [...this.openRepositories]; openRepositories.forEach(r => r.dispose()); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 472e8a2a18..86ccd958b1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -743,6 +743,15 @@ export class Repository implements Disposable { const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root)); onConfigListenerForUntracked(this.updateModelState, this, this.disposables); + const updateInputBoxVisibility = () => { + const config = workspace.getConfiguration('git', root); + this._sourceControl.inputBox.visible = config.get('showCommitInput', true); + }; + + const onConfigListenerForInputBoxVisibility = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.showCommitInput', root)); + onConfigListenerForInputBoxVisibility(updateInputBoxVisibility, this, this.disposables); + updateInputBoxVisibility(); + this.mergeGroup.hideWhenEmpty = true; this.untrackedGroup.hideWhenEmpty = true; diff --git a/extensions/github-authentication/.vscodeignore b/extensions/github-authentication/.vscodeignore new file mode 100644 index 0000000000..ee85b88450 --- /dev/null +++ b/extensions/github-authentication/.vscodeignore @@ -0,0 +1,8 @@ +src/** +!src/common/config.json +out/** +build/** +extension.webpack.config.js +tsconfig.json +yarn.lock +README.md diff --git a/extensions/github-authentication/build/postinstall.js b/extensions/github-authentication/build/generateconfig.js similarity index 100% rename from extensions/github-authentication/build/postinstall.js rename to extensions/github-authentication/build/generateconfig.js diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index bf2d01cbe3..54d30a027f 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -14,15 +14,16 @@ "activationEvents": [ "*" ], + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "main": "./out/extension.js", "scripts": { "vscode:prepublish": "npm run compile", "compile": "gulp compile-extension:github-authentication", - "watch": "gulp watch-extension:github-authentication", - "postinstall": "node build/postinstall.js" + "watch": "gulp watch-extension:github-authentication" }, "dependencies": { "uuid": "^3.3.3", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.2" }, "devDependencies": { diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index 3c41a77f21..25271ae17b 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -7,8 +7,11 @@ import * as vscode from 'vscode'; import { GitHubAuthenticationProvider, onDidChangeSessions } from './github'; import { uriHandler } from './githubServer'; import Logger from './common/logger'; +import TelemetryReporter from 'vscode-extension-telemetry'; export async function activate(context: vscode.ExtensionContext) { + const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string }; + const telemetryReporter = new TelemetryReporter(name, version, aiKey); context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); const loginService = new GitHubAuthenticationProvider(); @@ -22,19 +25,29 @@ export async function activate(context: vscode.ExtensionContext) { getSessions: () => Promise.resolve(loginService.sessions), login: async (scopeList: string[]) => { try { - const session = await loginService.login(scopeList.join(' ')); + telemetryReporter.sendTelemetryEvent('login'); + const session = await loginService.login(scopeList.sort().join(' ')); Logger.info('Login success!'); onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return session; } catch (e) { + telemetryReporter.sendTelemetryEvent('loginFailed'); vscode.window.showErrorMessage(`Sign in failed: ${e}`); Logger.error(e); throw e; } }, logout: async (id: string) => { - await loginService.logout(id); - onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); + try { + telemetryReporter.sendTelemetryEvent('logout'); + await loginService.logout(id); + onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); + } catch (e) { + telemetryReporter.sendTelemetryEvent('logoutFailed'); + vscode.window.showErrorMessage(`Sign out failed: ${e}`); + Logger.error(e); + throw e; + } } }); diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 7a403536a8..fc204a1a34 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -12,12 +12,27 @@ import Logger from './common/logger'; export const onDidChangeSessions = new vscode.EventEmitter(); interface SessionData { + id: string; + account?: { + displayName: string; + id: string; + } + scopes: string[]; + accessToken: string; +} + +// TODO remove +interface OldSessionData { id: string; accountName: string; scopes: string[]; accessToken: string; } +function isOldSessionData(x: any): x is OldSessionData { + return !!x.accountName; +} + export class GitHubAuthenticationProvider { private _sessions: vscode.AuthenticationSession[] = []; private _githubServer = new GitHubServer(); @@ -68,15 +83,34 @@ export class GitHubAuthenticationProvider { const storedSessions = await keychain.getToken(); if (storedSessions) { try { - const sessionData: SessionData[] = JSON.parse(storedSessions); - return sessionData.map(session => { - return { - id: session.id, - accountName: session.accountName, - scopes: session.scopes, - getAccessToken: () => Promise.resolve(session.accessToken) - }; + const sessionData: (SessionData | OldSessionData)[] = JSON.parse(storedSessions); + const sessionPromises = sessionData.map(async (session: SessionData | OldSessionData): Promise => { + try { + const needsUserInfo = isOldSessionData(session) || !session.account; + let userInfo: { id: string, accountName: string }; + if (needsUserInfo) { + userInfo = await this._githubServer.getUserInfo(session.accessToken); + } + + return { + id: session.id, + account: { + displayName: isOldSessionData(session) + ? session.accountName + : session.account?.displayName ?? userInfo!.accountName, + id: isOldSessionData(session) + ? userInfo!.id + : session.account?.id ?? userInfo!.id + }, + scopes: session.scopes, + getAccessToken: () => Promise.resolve(session.accessToken) + }; + } catch (e) { + return undefined; + } }); + + return (await Promise.all(sessionPromises)).filter((x: vscode.AuthenticationSession | undefined): x is vscode.AuthenticationSession => !!x); } catch (e) { Logger.error(`Error reading sessions: ${e}`); } @@ -90,7 +124,7 @@ export class GitHubAuthenticationProvider { const resolvedAccessToken = await session.getAccessToken(); return { id: session.id, - accountName: session.accountName, + account: session.account, scopes: session.scopes, accessToken: resolvedAccessToken }; @@ -125,7 +159,10 @@ export class GitHubAuthenticationProvider { return { id: uuid(), getAccessToken: () => Promise.resolve(token), - accountName: userInfo.accountName, + account: { + displayName: userInfo.accountName, + id: userInfo.id + }, scopes: scopes }; } @@ -137,7 +174,7 @@ export class GitHubAuthenticationProvider { this._sessions.push(session); } - this.storeSessions(); + await this.storeSessions(); } public async logout(id: string) { @@ -145,9 +182,13 @@ export class GitHubAuthenticationProvider { if (sessionIndex > -1) { const session = this._sessions.splice(sessionIndex, 1)[0]; const token = await session.getAccessToken(); - await this._githubServer.revokeToken(token); + try { + await this._githubServer.revokeToken(token); + } catch (_) { + // ignore, should still remove from keychain + } } - this.storeSessions(); + await this.storeSessions(); } } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 8eb54dcb30..890993950b 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -145,6 +145,7 @@ export class GitHubServer { Logger.info('Got account info!'); resolve({ id: json.id, accountName: json.login }); } else { + Logger.error('Getting account info failed'); reject(new Error(result.statusMessage)); } }); @@ -186,6 +187,7 @@ export class GitHubServer { Logger.info('Revoked token!'); resolve(); } else { + Logger.info(`Revoking token failed: ${result.statusMessage}`); reject(new Error(result.statusMessage)); } }); diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index c1f0b96f5b..5734d746bc 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -29,6 +29,15 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== + dependencies: + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.6" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -91,6 +100,18 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +diagnostic-channel-publishers@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -314,7 +335,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -semver@^5.4.1: +semver@^5.3.0, semver@^5.4.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -436,6 +457,13 @@ uuid@^3.3.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== + dependencies: + applicationinsights "1.0.8" + vscode-nls@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" @@ -457,3 +485,8 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +zone.js@0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index 83ec387ce1..0c5aedf6e2 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -23,7 +23,9 @@ export function activate(context: vscode.ExtensionContext) { const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); - context.subscriptions.push(vscode.window.registerCustomEditorProvider2(PreviewManager.viewType, previewManager)); + context.subscriptions.push(vscode.window.registerCustomEditorProvider2(PreviewManager.viewType, previewManager, { + supportsMultipleEditorsPerResource: true, + })); context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => { previewManager.activePreview?.zoomIn(); @@ -33,4 +35,3 @@ export function activate(context: vscode.ExtensionContext) { previewManager.activePreview?.zoomOut(); })); } - diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 9686329f2b..b61e029bd7 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -28,7 +28,7 @@ export class PreviewManager implements vscode.CustomEditorProvider { ) { } public async openCustomDocument(uri: vscode.Uri) { - return new vscode.CustomDocument(uri); + return { uri, dispose: () => { } }; } public async resolveCustomEditor( diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 38a2bbcc93..b7a874b2d9 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -13,12 +13,12 @@ const localize = nls.loadMessageBundle(); import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, } from 'vscode'; import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -153,25 +153,41 @@ export function activate(context: ExtensionContext) { }, // testing the replace / insert mode provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { - function updateRanges(item: CompletionItem) { + function update(item: CompletionItem) { const range = item.range; if (range instanceof Range && range.end.isAfter(position) && range.start.isBeforeOrEqual(position)) { item.range = { inserting: new Range(range.start, position), replacing: range }; } + if (item.documentation instanceof MarkdownString) { + item.documentation = updateMarkdownString(item.documentation); + } + } function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { if (r) { - (Array.isArray(r) ? r : r.items).forEach(updateRanges); + (Array.isArray(r) ? r : r.items).forEach(update); } return r; } - const isThenable = (obj: ProviderResult): obj is Thenable => obj && (obj)['then']; const r = next(document, position, context, token); if (isThenable(r)) { return r.then(updateProposals); } return updateProposals(r); + }, + provideHover(document: TextDocument, position: Position, token: CancellationToken, next: ProvideHoverSignature) { + function updateHover(r: Hover | null | undefined): Hover | null | undefined { + if (r && Array.isArray(r.contents)) { + r.contents = r.contents.map(h => h instanceof MarkdownString ? updateMarkdownString(h) : h); + } + return r; + } + const r = next(document, position, token); + if (isThenable(r)) { + return r.then(updateHover); + } + return updateHover(r); } } }; @@ -492,3 +508,13 @@ function readJSONFile(location: string) { return {}; } } + +function isThenable(obj: ProviderResult): obj is Thenable { + return obj && (obj)['then']; +} + +function updateMarkdownString(h: MarkdownString): MarkdownString { + const n = new MarkdownString(h.value, true); + n.isTrusted = h.isTrusted; + return n; +} diff --git a/extensions/json/package.json b/extensions/json/package.json index 8f34baf148..901584e1d9 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -22,13 +22,13 @@ "extensions": [ ".json", ".bowerrc", - ".jshintrc", ".jscsrc", - ".swcrc", ".webmanifest", ".js.map", ".css.map", - ".har" + ".har", + ".jslintrc", + ".jsonld" ], "filenames": [ "composer.lock", @@ -51,7 +51,10 @@ ".babelrc", ".jsonc", ".eslintrc", - ".eslintrc.json" + ".eslintrc.json", + ".jsfmtrc", + ".jshintrc", + ".swcrc" ], "configuration": "./language-configuration.json" } diff --git a/extensions/machine-learning-services/src/test/mainController.test.ts b/extensions/machine-learning-services/src/test/mainController.test.ts index 815d7e13fa..6b7f2b9132 100644 --- a/extensions/machine-learning-services/src/test/mainController.test.ts +++ b/extensions/machine-learning-services/src/test/mainController.test.ts @@ -68,7 +68,8 @@ function createContext(): TestContext { packageJSON: '', extensionKind: vscode.ExtensionKind.UI, exports: extensionApi, - activate: () => {return Promise.resolve();} + activate: () => {return Promise.resolve();}, + extensionUri: vscode.Uri.parse('') }, apiWrapper: TypeMoq.Mock.ofType(ApiWrapper), queryRunner: TypeMoq.Mock.ofType(QueryRunner), @@ -88,7 +89,8 @@ function createContext(): TestContext { asAbsolutePath: () => {return '';}, storagePath: '', globalStoragePath: '', - logPath: '' + logPath: '', + extensionUri: vscode.Uri.parse('') }, outputChannel: { name: '', @@ -106,7 +108,8 @@ function createContext(): TestContext { packageJSON: {}, extensionKind: vscode.ExtensionKind.UI, exports: {}, - activate: () => { return Promise.resolve(); } + activate: () => { return Promise.resolve(); }, + extensionUri: vscode.Uri.parse('') }, workspaceConfig: { get: () => {return 'value';}, diff --git a/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts b/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts index 2aa6305d58..e99badf84a 100644 --- a/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts +++ b/extensions/machine-learning-services/src/test/views/dashboardWidget.test.ts @@ -33,7 +33,7 @@ describe('Dashboard widget', () => { let testContext = createContext(); const dashboard = new DashboardWidget(testContext.apiWrapper.object, ''); dashboard.register(); - testContext.onClick.fire(); + testContext.onClick.fire(undefined); testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce()); }); }); diff --git a/extensions/machine-learning-services/src/test/views/externalLanguages/addEditLanguageTab.test.ts b/extensions/machine-learning-services/src/test/views/externalLanguages/addEditLanguageTab.test.ts index 3f0eefa622..6a75313677 100644 --- a/extensions/machine-learning-services/src/test/views/externalLanguages/addEditLanguageTab.test.ts +++ b/extensions/machine-learning-services/src/test/views/externalLanguages/addEditLanguageTab.test.ts @@ -111,7 +111,7 @@ describe('Add Edit External Languages Tab', () => { }); }); - testContext.onClick.fire(); + testContext.onClick.fire(undefined); parent.onUpdatedLanguage(languageUpdateModel); await promise; should.equal(updateCalled, true); diff --git a/extensions/machine-learning-services/src/test/views/models/predictWizard.test.ts b/extensions/machine-learning-services/src/test/views/models/predictWizard.test.ts index 20bc31120d..fe3897ea09 100644 --- a/extensions/machine-learning-services/src/test/views/models/predictWizard.test.ts +++ b/extensions/machine-learning-services/src/test/views/models/predictWizard.test.ts @@ -183,7 +183,7 @@ describe('Predict Wizard', () => { view.modelBrowsePage.modelSourceType = ModelSourceType.RegisteredModels; } await view.refresh(); - testContext.onClick.fire(); + testContext.onClick.fire(undefined); should.equal(view.modelSourcePage?.data, ModelSourceType.RegisteredModels); should.notEqual(view.localModelsComponent?.data, undefined); diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index 26a3599cf5..bbc5e342db 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -85,11 +85,11 @@ "snippets": [ { "language": "markdown", - "path": "./snippets/markdown.json" + "path": "./snippets/markdown.code-snippets" } ] }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js microsoft/vscode-markdown-tm-grammar syntaxes/markdown.tmLanguage ./syntaxes/markdown.tmLanguage.json" } -} \ No newline at end of file +} diff --git a/extensions/markdown-basics/snippets/markdown.json b/extensions/markdown-basics/snippets/markdown.code-snippets similarity index 100% rename from extensions/markdown-basics/snippets/markdown.json rename to extensions/markdown-basics/snippets/markdown.code-snippets diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 1aa7530d34..0762949b41 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 r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},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=2)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let o=void 0;function r(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=r,t.getSettings=function(){if(o)return o;if(o=r("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),r="code-line";function i(e){return t=0,n=o.getSettings().lineCount-1,r=e,Math.min(n,Math.max(t,r));var t,n,r}const s=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName(r)){const n=+t.getAttribute("data-line");isNaN(n)||("CODE"===t.tagName&&t.parentElement&&"PRE"===t.parentElement.tagName?e.push({element:t.parentElement,line:n}):e.push({element:t,line:n}))}}return e}})();function a(e){const t=Math.floor(e),n=s();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=s(),n=e-window.scrollY;let o=-1,r=t.length-1;for(;o+1=n?r=e:o=e}const i=t[r],a=u(i);if(r>=1&&a.top>n){return{previous:t[o],next:i}}return r>1&&rn?{previous:i,next:t[r+1]}:{previous:i}}function u({element:e}){const t=e.getBoundingClientRect(),n=e.querySelector(`.${r}`);if(n){const e=n.getBoundingClientRect(),o=Math.max(1,e.top-t.top);return{top:t.top,height:o}}return t}t.getElementsForSourceLine=a,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}=a(e);if(!t)return;let r=0;const i=u(t),s=i.top;if(n&&n.line!==t.line){r=s+(e-t.line)/(n.line-t.line)*(n.element.getBoundingClientRect().top-s)}else{const t=e-Math.floor(e);r=s+i.height*t}window.scroll(window.scrollX,Math.max(1,window.scrollY+r))},t.getEditorLineNumberForPageOffset=function(e){const{previous:t,next:n}=c(e);if(t){const o=u(t),r=e-window.scrollY-o.top;if(n){const e=r/(u(n).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 s().find(t=>t.element.id===e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(3),r=n(4),i=n(5),s=n(1),a=n(0),c=n(6);let u=!0;const l=new o.ActiveLineMarker,f=a.getSettings(),d=acquireVsCodeApi();let g=a.getData("data-state");d.setState(g);const p=i.createPosterForVsCode(d);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=()=>{v()},r.onceDocumentLoaded(()=>{f.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+f.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=c(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.line=t,e(t))}})();let v=c(()=>{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===f.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,f)}},!1),document.addEventListener("dblclick",e=>{if(!f.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",c(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,d.setState(g))}},50))},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}),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(0);t.createPosterForVsCode=e=>new class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}}},function(e,t,n){(function(t){var n="Expected a function",o=NaN,r="[object Symbol]",i=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,f="object"==typeof self&&self&&self.Object===Object&&self,d=l||f||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return d.Date.now()};function h(e,t,o){var r,i,s,a,c,u,l=0,f=!1,d=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=r,o=i;return r=i=void 0,l=t,a=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||d&&e-l>=s}function E(){var e=v();if(y(e))return M(e);c=setTimeout(E,function(e){var n=t-(e-u);return d?m(n,s-(e-l)):n}(e))}function M(e){return c=void 0,g&&r?h(e):(r=i=void 0,a)}function S(){var e=v(),n=y(e);if(r=arguments,i=this,u=e,n){if(void 0===c)return function(e){return l=e,c=setTimeout(E,t),f?h(e):a}(u);if(d)return c=setTimeout(E,t),h(u)}return void 0===c&&(c=setTimeout(E,t)),a}return t=b(t)||0,w(o)&&(f=!!o.leading,s=(d="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),S.cancel=function(){void 0!==c&&clearTimeout(c),l=0,r=u=i=c=void 0},S.flush=function(){return void 0===c?a:M(v())},S}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)==r}(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(i,"");var n=a.test(e);return n||c.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var r=!0,i=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(r="leading"in o?!!o.leading:r,i="trailing"in o?!!o.trailing:i),h(e,t,{leading:r,maxWait:t,trailing:i})}}).call(this,n(7))},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvbWVzc2FnaW5nLnRzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2Rhc2gudGhyb3R0bGUvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJTeW1ib2wiLCJ0b1N0cmluZ1RhZyIsInZhbHVlIiwidCIsIm1vZGUiLCJfX2VzTW9kdWxlIiwibnMiLCJjcmVhdGUiLCJrZXkiLCJiaW5kIiwibiIsIm9iamVjdCIsInByb3BlcnR5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJwIiwicyIsImNhY2hlZFNldHRpbmdzIiwidW5kZWZpbmVkIiwiZ2V0RGF0YSIsImVsZW1lbnQiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwiZGF0YSIsImdldEF0dHJpYnV0ZSIsIkpTT04iLCJwYXJzZSIsIkVycm9yIiwiZ2V0U2V0dGluZ3MiLCJzZXR0aW5nc18xIiwiY29kZUxpbmVDbGFzcyIsImNsYW1wTGluZSIsImxpbmUiLCJtaW4iLCJtYXgiLCJsaW5lQ291bnQiLCJNYXRoIiwiZ2V0Q29kZUxpbmVFbGVtZW50cyIsImVsZW1lbnRzIiwiYm9keSIsImdldEVsZW1lbnRzQnlDbGFzc05hbWUiLCJpc05hTiIsInRhZ05hbWUiLCJwYXJlbnRFbGVtZW50IiwicHVzaCIsImdldEVsZW1lbnRzRm9yU291cmNlTGluZSIsInRhcmdldExpbmUiLCJsaW5lTnVtYmVyIiwiZmxvb3IiLCJsaW5lcyIsInByZXZpb3VzIiwiZW50cnkiLCJuZXh0IiwiZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0Iiwib2Zmc2V0IiwicG9zaXRpb24iLCJ3aW5kb3ciLCJzY3JvbGxZIiwibG8iLCJoaSIsImxlbmd0aCIsIm1pZCIsImJvdW5kcyIsImdldEVsZW1lbnRCb3VuZHMiLCJ0b3AiLCJoZWlnaHQiLCJoaUVsZW1lbnQiLCJoaUJvdW5kcyIsIm15Qm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiY29kZUxpbmVDaGlsZCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGlsZEJvdW5kcyIsInNjcm9sbFRvUmV2ZWFsU291cmNlTGluZSIsInNjcm9sbFByZXZpZXdXaXRoRWRpdG9yIiwic2Nyb2xsIiwic2Nyb2xsWCIsInNjcm9sbFRvIiwicmVjdCIsInByZXZpb3VzVG9wIiwicHJvZ3Jlc3NJbkVsZW1lbnQiLCJnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCIsInByZXZpb3VzQm91bmRzIiwib2Zmc2V0RnJvbVByZXZpb3VzIiwicHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMiLCJwcm9ncmVzc1dpdGhpbkVsZW1lbnQiLCJnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50IiwiZnJhZ21lbnQiLCJmaW5kIiwiaWQiLCJhY3RpdmVMaW5lTWFya2VyXzEiLCJldmVudHNfMSIsIm1lc3NhZ2luZ18xIiwic2Nyb2xsX3N5bmNfMSIsInRocm90dGxlIiwic2Nyb2xsRGlzYWJsZWQiLCJtYXJrZXIiLCJBY3RpdmVMaW5lTWFya2VyIiwic2V0dGluZ3MiLCJ2c2NvZGUiLCJhY3F1aXJlVnNDb2RlQXBpIiwic3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsIm9uY2VEb2N1bWVudExvYWRlZCIsInNldFRpbWVvdXQiLCJpbml0aWFsTGluZSIsIm9uVXBkYXRlVmlldyIsImRvU2Nyb2xsIiwiaW1hZ2VJbmZvIiwiaW1hZ2VzIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJpbWciLCJjbGFzc0xpc3QiLCJjb250YWlucyIsInJlbW92ZSIsIndpZHRoIiwicG9zdE1lc3NhZ2UiLCJhZGRFdmVudExpc3RlbmVyIiwiZXZlbnQiLCJzb3VyY2UiLCJ0eXBlIiwib25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uIiwiZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yIiwibm9kZSIsInRhcmdldCIsInBhcmVudE5vZGUiLCJwYWdlWSIsInBhc3NUaHJvdWdoTGlua1NjaGVtZXMiLCJocmVmIiwic3RhcnRzV2l0aCIsInNvbWUiLCJzY2hlbWUiLCJocmVmVGV4dCIsInRlc3QiLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsInRoaXMiLCJfdXBkYXRlIiwiYmVmb3JlIiwiX3VubWFya0FjdGl2ZUVsZW1lbnQiLCJfY3VycmVudCIsIl9tYXJrQWN0aXZlRWxlbWVudCIsImNsYXNzTmFtZSIsInJlcGxhY2UiLCJmIiwicmVhZHlTdGF0ZSIsIkZVTkNfRVJST1JfVEVYVCIsIk5BTiIsInN5bWJvbFRhZyIsInJlVHJpbSIsInJlSXNCYWRIZXgiLCJyZUlzQmluYXJ5IiwicmVJc09jdGFsIiwiZnJlZVBhcnNlSW50IiwicGFyc2VJbnQiLCJmcmVlR2xvYmFsIiwiZ2xvYmFsIiwiZnJlZVNlbGYiLCJzZWxmIiwicm9vdCIsIkZ1bmN0aW9uIiwib2JqZWN0VG9TdHJpbmciLCJ0b1N0cmluZyIsIm5hdGl2ZU1heCIsIm5hdGl2ZU1pbiIsIm5vdyIsIkRhdGUiLCJkZWJvdW5jZSIsImZ1bmMiLCJ3YWl0Iiwib3B0aW9ucyIsImxhc3RBcmdzIiwibGFzdFRoaXMiLCJtYXhXYWl0IiwicmVzdWx0IiwidGltZXJJZCIsImxhc3RDYWxsVGltZSIsImxhc3RJbnZva2VUaW1lIiwibGVhZGluZyIsIm1heGluZyIsInRyYWlsaW5nIiwiVHlwZUVycm9yIiwiaW52b2tlRnVuYyIsInRpbWUiLCJhcmdzIiwidGhpc0FyZyIsImFwcGx5Iiwic2hvdWxkSW52b2tlIiwidGltZVNpbmNlTGFzdENhbGwiLCJ0aW1lckV4cGlyZWQiLCJ0cmFpbGluZ0VkZ2UiLCJyZW1haW5pbmdXYWl0IiwiZGVib3VuY2VkIiwiaXNJbnZva2luZyIsImFyZ3VtZW50cyIsImxlYWRpbmdFZGdlIiwidG9OdW1iZXIiLCJpc09iamVjdCIsImNhbmNlbCIsImNsZWFyVGltZW91dCIsImZsdXNoIiwiaXNPYmplY3RMaWtlIiwiaXNTeW1ib2wiLCJvdGhlciIsInZhbHVlT2YiLCJpc0JpbmFyeSIsInNsaWNlIiwiZyIsImUiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZ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QUFXdEQsRUFBV2xCLEtBQUsxSSxHQUMvQixPQUFRa04sR0FBWXJELEVBQVVuQixLQUFLMUksR0FDL0I4SixFQUFhOUosRUFBTW1OLE1BQU0sR0FBSUQsRUFBVyxFQUFJLEdBQzNDdkQsRUFBV2pCLEtBQUsxSSxHQUFTd0osR0FBT3hKLEVBR3ZDbEIsRUFBT0QsUUE5SVAsU0FBa0JnTSxFQUFNQyxFQUFNQyxHQUM1QixJQUFJUSxHQUFVLEVBQ1ZFLEdBQVcsRUFFZixHQUFtQixtQkFBUlosRUFDVCxNQUFNLElBQUlhLFVBQVVuQyxHQU10QixPQUpJbUQsRUFBUzNCLEtBQ1hRLEVBQVUsWUFBYVIsSUFBWUEsRUFBUVEsUUFBVUEsRUFDckRFLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FFbkRiLEVBQVNDLEVBQU1DLEVBQU0sQ0FDMUIsUUFBV1MsRUFDWCxRQUFXVCxFQUNYLFNBQVlXLE8sK0JDdFRoQixJQUFJMkIsRUFHSkEsRUFBSSxXQUNILE9BQU92RSxLQURKLEdBSUosSUFFQ3VFLEVBQUlBLEdBQUssSUFBSS9DLFNBQVMsY0FBYixHQUNSLE1BQU9nRCxHQUVjLGlCQUFYL0osU0FBcUI4SixFQUFJOUosUUFPckN4RSxFQUFPRCxRQUFVdU8iLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMik7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IGNvZGVMaW5lQ2xhc3MgPSAnY29kZS1saW5lJztcbmZ1bmN0aW9uIGNsYW1wKG1pbiwgbWF4LCB2YWx1ZSkge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lKSB7XG4gICAgcmV0dXJuIGNsYW1wKDAsIHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuICAgIGxldCBlbGVtZW50cztcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBpZiAoIWVsZW1lbnRzKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShjb2RlTGluZUNsYXNzKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuICAgICAgICAgICAgICAgIGlmIChpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQudGFnTmFtZSA9PT0gJ0NPREUnICYmIGVsZW1lbnQucGFyZW50RWxlbWVudCAmJiBlbGVtZW50LnBhcmVudEVsZW1lbnQudGFnTmFtZSA9PT0gJ1BSRScpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gRmVuY2hlZCBjb2RlIGJsb2NrcyBhcmUgYSBzcGVjaWFsIGNhc2Ugc2luY2UgdGhlIGBjb2RlLWxpbmVgIGNhbiBvbmx5IGJlIG1hcmtlZCBvblxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgYDxjb2RlPmAgZWxlbWVudCBhbmQgbm90IHRoZSBwYXJlbnQgYDxwcmU+YCBlbGVtZW50LlxuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudC5wYXJlbnRFbGVtZW50LCBsaW5lIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQsIGxpbmUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBlbGVtZW50cztcbiAgICB9O1xufSkoKTtcbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5mdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZSkge1xuICAgIGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuICAgIGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuICAgIGxldCBwcmV2aW91cyA9IGxpbmVzWzBdIHx8IG51bGw7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuICAgICAgICBpZiAoZW50cnkubGluZSA9PT0gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGVudHJ5LCBuZXh0OiB1bmRlZmluZWQgfTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG4gICAgICAgIH1cbiAgICAgICAgcHJldmlvdXMgPSBlbnRyeTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cbmV4cG9ydHMuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgY29uc3QgcG9zaXRpb24gPSBvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWTtcbiAgICBsZXQgbG8gPSAtMTtcbiAgICBsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuICAgIHdoaWxlIChsbyArIDEgPCBoaSkge1xuICAgICAgICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuICAgICAgICBjb25zdCBib3VuZHMgPSBnZXRFbGVtZW50Qm91bmRzKGxpbmVzW21pZF0pO1xuICAgICAgICBpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcbiAgICAgICAgICAgIGhpID0gbWlkO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbG8gPSBtaWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuICAgIGNvbnN0IGhpQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhoaUVsZW1lbnQpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIGlmIChoaSA+IDEgJiYgaGkgPCBsaW5lcy5sZW5ndGggJiYgaGlCb3VuZHMudG9wICsgaGlCb3VuZHMuaGVpZ2h0ID4gcG9zaXRpb24pIHtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCwgbmV4dDogbGluZXNbaGkgKyAxXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldDtcbmZ1bmN0aW9uIGdldEVsZW1lbnRCb3VuZHMoeyBlbGVtZW50IH0pIHtcbiAgICBjb25zdCBteUJvdW5kcyA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgLy8gU29tZSBjb2RlIGxpbmUgZWxlbWVudHMgbWF5IGNvbnRhaW4gb3RoZXIgY29kZSBsaW5lIGVsZW1lbnRzLlxuICAgIC8vIEluIHRob3NlIGNhc2VzLCBvbmx5IHRha2UgdGhlIGhlaWdodCB1cCB0byB0aGF0IGNoaWxkLlxuICAgIGNvbnN0IGNvZGVMaW5lQ2hpbGQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoYC4ke2NvZGVMaW5lQ2xhc3N9YCk7XG4gICAgaWYgKGNvZGVMaW5lQ2hpbGQpIHtcbiAgICAgICAgY29uc3QgY2hpbGRCb3VuZHMgPSBjb2RlTGluZUNoaWxkLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBoZWlnaHQgPSBNYXRoLm1heCgxLCAoY2hpbGRCb3VuZHMudG9wIC0gbXlCb3VuZHMudG9wKSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0b3A6IG15Qm91bmRzLnRvcCxcbiAgICAgICAgICAgIGhlaWdodDogaGVpZ2h0XG4gICAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiBteUJvdW5kcztcbn1cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmUpIHtcbiAgICBpZiAoIXNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChsaW5lIDw9IDApIHtcbiAgICAgICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgIGlmICghcHJldmlvdXMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgc2Nyb2xsVG8gPSAwO1xuICAgIGNvbnN0IHJlY3QgPSBnZXRFbGVtZW50Qm91bmRzKHByZXZpb3VzKTtcbiAgICBjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuICAgIGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuICAgICAgICAvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuICAgICAgICBjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICBjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IHByb2dyZXNzSW5FbGVtZW50ID0gbGluZSAtIE1hdGguZmxvb3IobGluZSk7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG4gICAgfVxuICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cbmV4cG9ydHMuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lID0gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lO1xuZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHByZXZpb3VzKSB7XG4gICAgICAgIGNvbnN0IHByZXZpb3VzQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhwcmV2aW91cyk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChnZXRFbGVtZW50Qm91bmRzKG5leHQpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuZXhwb3J0cy5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0O1xuLyoqXG4gKiBUcnkgdG8gZmluZCB0aGUgaHRtbCBlbGVtZW50IGJ5IHVzaW5nIGEgZnJhZ21lbnQgaWRcbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudChmcmFnbWVudCkge1xuICAgIHJldHVybiBnZXRDb2RlTGluZUVsZW1lbnRzKCkuZmluZCgoZWxlbWVudCkgPT4ge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcbiAgICB9KTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG4gICAgICAgICAgICBpZiAoc3RhdGUuZnJhZ21lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHN0YXRlLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcbiAgICAgICAgICAgICAgICBpZiAoIWlzTmFOKGluaXRpYWxMaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sIDApO1xuICAgIH1cbn0pO1xuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcbiAgICBjb25zdCBkb1Njcm9sbCA9IHRocm90dGxlKChsaW5lKSA9PiB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgc2Nyb2xsX3N5bmNfMS5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG4gICAgfSwgNTApO1xuICAgIHJldHVybiAobGluZSwgc2V0dGluZ3MpID0+IHtcbiAgICAgICAgaWYgKCFpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgc2V0dGluZ3MubGluZSA9IGxpbmU7XG4gICAgICAgICAgICBkb1Njcm9sbChsaW5lKTtcbiAgICAgICAgfVxuICAgIH07XG59KSgpO1xubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG4gICAgY29uc3QgaW1hZ2VJbmZvID0gW107XG4gICAgbGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcbiAgICBpZiAoaW1hZ2VzKSB7XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgaW1hZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBpbWcgPSBpbWFnZXNbaV07XG4gICAgICAgICAgICBpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG4gICAgICAgICAgICAgICAgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2xvYWRpbmcnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGltYWdlSW5mby5wdXNoKHtcbiAgICAgICAgICAgICAgICBpZDogaW1nLmlkLFxuICAgICAgICAgICAgICAgIGhlaWdodDogaW1nLmhlaWdodCxcbiAgICAgICAgICAgICAgICB3aWR0aDogaW1nLndpZHRoXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG4gICAgfVxufSwgNTApO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcbiAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG4gICAgICAgICAgICBicmVhaztcbiAgICB9XG59LCBmYWxzZSk7XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3NcbiAgICBmb3IgKGxldCBub2RlID0gZXZlbnQudGFyZ2V0OyBub2RlOyBub2RlID0gbm9kZS5wYXJlbnROb2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcbiAgICB9XG59KTtcbmNvbnN0IHBhc3NUaHJvdWdoTGlua1NjaGVtZXMgPSBbJ2h0dHA6JywgJ2h0dHBzOicsICdtYWlsdG86JywgJ3ZzY29kZTonLCAndnNjb2RlLWluc2lkZXJzOiddO1xuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG4gICAgaWYgKCFldmVudCkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBub2RlID0gZXZlbnQudGFyZ2V0O1xuICAgIHdoaWxlIChub2RlKSB7XG4gICAgICAgIGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG4gICAgICAgICAgICBpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBQYXNzIHRocm91Z2gga25vd24gc2NoZW1lc1xuICAgICAgICAgICAgaWYgKHBhc3NUaHJvdWdoTGlua1NjaGVtZXMuc29tZShzY2hlbWUgPT4gbm9kZS5ocmVmLnN0YXJ0c1dpdGgoc2NoZW1lKSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBocmVmVGV4dCA9IG5vZGUuZ2V0QXR0cmlidXRlKCdkYXRhLWhyZWYnKSB8fCBub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpO1xuICAgICAgICAgICAgLy8gSWYgb3JpZ2luYWwgbGluayBkb2Vzbid0IGxvb2sgbGlrZSBhIHVybCwgZGVsZWdhdGUgYmFjayB0byBWUyBDb2RlIHRvIHJlc29sdmVcbiAgICAgICAgICAgIGlmICghL15bYS16XFwtXSs6L2kudGVzdChocmVmVGV4dCkpIHtcbiAgICAgICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ29wZW5MaW5rJywgeyBocmVmOiBocmVmVGV4dCB9KTtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuICAgIH1cbn0sIHRydWUpO1xud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcbiAgICBpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcbiAgICAgICAgc2Nyb2xsRGlzYWJsZWQgPSBmYWxzZTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IGxpbmUgPSBzY3JvbGxfc3luY18xLmdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KHdpbmRvdy5zY3JvbGxZKTtcbiAgICAgICAgaWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgncmV2ZWFsTGluZScsIHsgbGluZSB9KTtcbiAgICAgICAgICAgIHN0YXRlLmxpbmUgPSBsaW5lO1xuICAgICAgICAgICAgdnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbiAgICAgICAgfVxuICAgIH1cbn0sIDUwKSk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3Qgc2Nyb2xsX3N5bmNfMSA9IHJlcXVpcmUoXCIuL3Njcm9sbC1zeW5jXCIpO1xuY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG4gICAgb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmUpIHtcbiAgICAgICAgY29uc3QgeyBwcmV2aW91cyB9ID0gc2Nyb2xsX3N5bmNfMS5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcbiAgICB9XG4gICAgX3VwZGF0ZShiZWZvcmUpIHtcbiAgICAgICAgdGhpcy5fdW5tYXJrQWN0aXZlRWxlbWVudCh0aGlzLl9jdXJyZW50KTtcbiAgICAgICAgdGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcbiAgICAgICAgdGhpcy5fY3VycmVudCA9IGJlZm9yZTtcbiAgICB9XG4gICAgX3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9IGVsZW1lbnQuY2xhc3NOYW1lLnJlcGxhY2UoL1xcYmNvZGUtYWN0aXZlLWxpbmVcXGIvZywgJycpO1xuICAgIH1cbiAgICBfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudCkge1xuICAgICAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuICAgIH1cbn1cbmV4cG9ydHMuQWN0aXZlTGluZU1hcmtlciA9IEFjdGl2ZUxpbmVNYXJrZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGYpIHtcbiAgICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZik7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBmKCk7XG4gICAgfVxufVxuZXhwb3J0cy5vbmNlRG9jdW1lbnRMb2FkZWQgPSBvbmNlRG9jdW1lbnRMb2FkZWQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuZXhwb3J0cy5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUgPSAodnNjb2RlKSA9PiB7XG4gICAgcmV0dXJuIG5ldyBjbGFzcyB7XG4gICAgICAgIHBvc3RNZXNzYWdlKHR5cGUsIGJvZHkpIHtcbiAgICAgICAgICAgIHZzY29kZS5wb3N0TWVzc2FnZSh7XG4gICAgICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG4gICAgICAgICAgICAgICAgYm9keVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9O1xufTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xuXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxuZyA9IChmdW5jdGlvbigpIHtcblx0cmV0dXJuIHRoaXM7XG59KSgpO1xuXG50cnkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcblx0ZyA9IGcgfHwgbmV3IEZ1bmN0aW9uKFwicmV0dXJuIHRoaXNcIikoKTtcbn0gY2F0Y2ggKGUpIHtcblx0Ly8gVGhpcyB3b3JrcyBpZiB0aGUgd2luZG93IHJlZmVyZW5jZSBpcyBhdmFpbGFibGVcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XG59XG5cbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXG4vLyBlYXNpZXIgdG8gaGFuZGxlIHRoaXMgY2FzZS4gaWYoIWdsb2JhbCkgeyAuLi59XG5cbm1vZHVsZS5leHBvcnRzID0gZztcbiJdLCJzb3VyY2VSb290IjoiIn0= \ 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,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(o,i,function(t){return e[t]}.bind(null,i));return o},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=3)}([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){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0),i="code-line";function r(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const c=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName(i)){const n=+t.getAttribute("data-line");isNaN(n)||("CODE"===t.tagName&&t.parentElement&&"PRE"===t.parentElement.tagName?e.push({element:t.parentElement,line:n}):e.push({element:t,line:n}))}}return e}})();function s(e){const t=Math.floor(e),n=c();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 a(e){const t=c(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1=n?i=e:o=e}const r=t[i],s=u(r);if(i>=1&&s.top>n){return{previous:t[o],next:r}}return i>1&&in?{previous:r,next:t[i+1]}:{previous:r}}function u({element:e}){const t=e.getBoundingClientRect(),n=e.querySelector(`.${i}`);if(n){const e=n.getBoundingClientRect(),o=Math.max(1,e.top-t.top);return{top:t.top,height:o}}return t}t.getElementsForSourceLine=s,t.getLineElementsAtPageOffset=a,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=u(t),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}=a(e);if(t){const o=u(t),i=e-window.scrollY-o.top;if(n){const e=i/(u(n).top-o.top);return r(t.line+e*(n.line-t.line))}{const e=i/o.height;return r(t.line+e)}}return null},t.getLineElementForFragment=function(e){return c().find(t=>t.element.id===e)}},function(e,t,n){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const o=n(7),i=n(8),r=n(9),c=n(2),s=n(0),a=n(10);let u=!0;const l=new o.ActiveLineMarker,f=s.getSettings(),d=acquireVsCodeApi(),m={...d.getState(),...s.getData("data-state")};d.setState(m);const p=r.createPosterForVsCode(d);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=()=>{h()},i.onceDocumentLoaded(()=>{const t=m.scrollProgress;"number"!=typeof t||f.fragment?f.scrollPreviewWithEditor&&e(()=>{if(f.fragment){m.fragment=void 0,d.setState(m);const e=c.getLineElementForFragment(f.fragment);e&&(u=!0,c.scrollToRevealSourceLine(e.line))}else isNaN(f.line)||(u=!0,c.scrollToRevealSourceLine(f.line))}):e(()=>{u=!0,window.scrollTo(0,t*document.body.clientHeight)})});const g=(()=>{const e=a(e=>{u=!0,c.scrollToRevealSourceLine(e)},50);return t=>{isNaN(t)||(m.line=t,e(t))}})();let h=a(()=>{const e=[];let t=document.getElementsByTagName("img");if(t){let n;for(n=0;n{u=!0,y(),h()},!0),window.addEventListener("message",e=>{if(e.data.source===f.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":g(e.data.line)}},!1),document.addEventListener("dblclick",e=>{if(!f.doubleClickToSwitchToEditor)return;for(let t=e.target;t;t=t.parentNode)if("A"===t.tagName)return;const t=e.pageY,n=c.getEditorLineNumberForPageOffset(t);"number"!=typeof n||isNaN(n)||p.postMessage("didClick",{line:Math.floor(n)})});const v=["http:","https:","mailto:","vscode:","vscode-insiders:"];function y(){m.scrollProgress=window.scrollY/document.body.clientHeight,d.setState(m)}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(v.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(y(),u)u=!1;else{const e=c.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||p.postMessage("revealLine",{line:e})}},50))}).call(this,n(4).setImmediate)},function(e,t,n){(function(e){var o=Function.prototype.apply;function i(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new i(o.call(setTimeout,window,arguments),clearTimeout)},t.setInterval=function(){return new i(o.call(setInterval,window,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},i.prototype.unref=i.prototype.ref=function(){},i.prototype.close=function(){this._clearFn.call(window,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(5),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var o,i,r,c,s,a=1,u={},l=!1,f=e.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(e);d=d&&d.setTimeout?d:e,"[object process]"==={}.toString.call(e.process)?o=function(e){t.nextTick((function(){p(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((r=new MessageChannel).port1.onmessage=function(e){p(e.data)},o=function(e){r.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(i=f.documentElement,o=function(e){var t=f.createElement("script");t.onreadystatechange=function(){p(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):o=function(e){setTimeout(p,0,e)}:(c="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(c)&&p(+t.data.slice(c.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),o=function(t){e.postMessage(c+t,"*")}),d.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;nnew class{postMessage(t,n){e.postMessage({type:t,source:o.getSettings().source,body:n})}}},function(e,t,n){(function(t){var n="Expected a function",o=NaN,i="[object Symbol]",r=/^\s+|\s+$/g,c=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,f="object"==typeof self&&self&&self.Object===Object&&self,d=l||f||Function("return this")(),m=Object.prototype.toString,p=Math.max,g=Math.min,h=function(){return d.Date.now()};function v(e,t,o){var i,r,c,s,a,u,l=0,f=!1,d=!1,m=!0;if("function"!=typeof e)throw new TypeError(n);function v(t){var n=i,o=r;return i=r=void 0,l=t,s=e.apply(o,n)}function b(e){var n=e-u;return void 0===u||n>=t||n<0||d&&e-l>=c}function T(){var e=h();if(b(e))return E(e);a=setTimeout(T,function(e){var n=t-(e-u);return d?g(n,c-(e-l)):n}(e))}function E(e){return a=void 0,m&&i?v(e):(i=r=void 0,s)}function _(){var e=h(),n=b(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(T,t),f?v(e):s}(u);if(d)return a=setTimeout(T,t),v(u)}return void 0===a&&(a=setTimeout(T,t)),s}return t=w(t)||0,y(o)&&(f=!!o.leading,c=(d="maxWait"in o)?p(w(o.maxWait)||0,t):c,m="trailing"in o?!!o.trailing:m),_.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},_.flush=function(){return void 0===a?s:E(h())},_}function y(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function w(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&m.call(e)==i}(e))return o;if(y(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=y(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(r,"");var n=s.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):c.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return y(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),v(e,t,{leading:i,maxWait:t,trailing:r})}}).call(this,n(1))}]); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9pbmRleC50cyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvdGltZXJzLWJyb3dzZXJpZnkvbWFpbi5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvc2V0aW1tZWRpYXRlL3NldEltbWVkaWF0ZS5qcyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvcHJvY2Vzcy9icm93c2VyLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvbG9kYXNoLnRocm90dGxlL2luZGV4LmpzIl0sIm5hbWVzIjpbImluc3RhbGxlZE1vZHVsZXMiLCJfX3dlYnBhY2tfcmVxdWlyZV9fIiwibW9kdWxlSWQiLCJleHBvcnRzIiwibW9kdWxlIiwiaSIsImwiLCJtb2R1bGVzIiwiY2FsbCIsIm0iLCJjIiwiZCIsIm5hbWUiLCJnZXR0ZXIiLCJvIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJlbnVtZXJhYmxlIiwiZ2V0IiwiciIsIlN5bWJvbCIsInRvU3RyaW5nVGFnIiwidmFsdWUiLCJ0IiwibW9kZSIsIl9fZXNNb2R1bGUiLCJucyIsImNyZWF0ZSIsImtleSIsImJpbmQiLCJuIiwib2JqZWN0IiwicHJvcGVydHkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsInAiLCJzIiwiY2FjaGVkU2V0dGluZ3MiLCJ1bmRlZmluZWQiLCJnZXREYXRhIiwiZWxlbWVudCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJkYXRhIiwiZ2V0QXR0cmlidXRlIiwiSlNPTiIsInBhcnNlIiwiRXJyb3IiLCJnZXRTZXR0aW5ncyIsImciLCJ0aGlzIiwiRnVuY3Rpb24iLCJlIiwid2luZG93Iiwic2V0dGluZ3NfMSIsImNvZGVMaW5lQ2xhc3MiLCJjbGFtcExpbmUiLCJsaW5lIiwibWluIiwibWF4IiwibGluZUNvdW50IiwiTWF0aCIsImdldENvZGVMaW5lRWxlbWVudHMiLCJlbGVtZW50cyIsImJvZHkiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiaXNOYU4iLCJ0YWdOYW1lIiwicGFyZW50RWxlbWVudCIsInB1c2giLCJnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUiLCJ0YXJnZXRMaW5lIiwibGluZU51bWJlciIsImZsb29yIiwibGluZXMiLCJwcmV2aW91cyIsImVudHJ5IiwibmV4dCIsImdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCIsIm9mZnNldCIsInBvc2l0aW9uIiwic2Nyb2xsWSIsImxvIiwiaGkiLCJsZW5ndGgiLCJtaWQiLCJib3VuZHMiLCJnZXRFbGVtZW50Qm91bmRzIiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJteUJvdW5kcyIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsImNvZGVMaW5lQ2hpbGQiLCJxdWVyeVNlbGVjdG9yIiwiY2hpbGRCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiYWN0aXZlTGluZU1hcmtlcl8xIiwiZXZlbnRzXzEiLCJtZXNzYWdpbmdfMSIsInNjcm9sbF9zeW5jXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwiQWN0aXZlTGluZU1hcmtlciIsInNldHRpbmdzIiwidnNjb2RlIiwiYWNxdWlyZVZzQ29kZUFwaSIsInN0YXRlIiwiZ2V0U3RhdGUiLCJzZXRTdGF0ZSIsIm1lc3NhZ2luZyIsImNyZWF0ZVBvc3RlckZvclZzQ29kZSIsImNzcEFsZXJ0ZXIiLCJzZXRQb3N0ZXIiLCJzdHlsZUxvYWRpbmdNb25pdG9yIiwib25sb2FkIiwidXBkYXRlSW1hZ2VTaXplcyIsIm9uY2VEb2N1bWVudExvYWRlZCIsInNjcm9sbFByb2dyZXNzIiwic2V0SW1tZWRpYXRlIiwiY2xpZW50SGVpZ2h0Iiwib25VcGRhdGVWaWV3IiwiZG9TY3JvbGwiLCJpbWFnZUluZm8iLCJpbWFnZXMiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsImltZyIsImNsYXNzTGlzdCIsImNvbnRhaW5zIiwicmVtb3ZlIiwid2lkdGgiLCJwb3N0TWVzc2FnZSIsImFkZEV2ZW50TGlzdGVuZXIiLCJ1cGRhdGVTY3JvbGxQcm9ncmVzcyIsImV2ZW50Iiwic291cmNlIiwidHlwZSIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwicGFnZVkiLCJwYXNzVGhyb3VnaExpbmtTY2hlbWVzIiwiaHJlZiIsInN0YXJ0c1dpdGgiLCJzb21lIiwic2NoZW1lIiwiaHJlZlRleHQiLCJ0ZXN0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJUaW1lb3V0IiwiY2xlYXJGbiIsIl9pZCIsIl9jbGVhckZuIiwic2V0VGltZW91dCIsImFwcGx5IiwiYXJndW1lbnRzIiwiY2xlYXJUaW1lb3V0Iiwic2V0SW50ZXJ2YWwiLCJjbGVhckludGVydmFsIiwidGltZW91dCIsImNsb3NlIiwidW5yZWYiLCJyZWYiLCJlbnJvbGwiLCJpdGVtIiwibXNlY3MiLCJfaWRsZVRpbWVvdXRJZCIsIl9pZGxlVGltZW91dCIsInVuZW5yb2xsIiwiX3VucmVmQWN0aXZlIiwiYWN0aXZlIiwiX29uVGltZW91dCIsInNlbGYiLCJnbG9iYWwiLCJjbGVhckltbWVkaWF0ZSIsInJlZ2lzdGVySW1tZWRpYXRlIiwiaHRtbCIsImNoYW5uZWwiLCJtZXNzYWdlUHJlZml4Iiwib25HbG9iYWxNZXNzYWdlIiwibmV4dEhhbmRsZSIsInRhc2tzQnlIYW5kbGUiLCJjdXJyZW50bHlSdW5uaW5nQVRhc2siLCJkb2MiLCJhdHRhY2hUbyIsImdldFByb3RvdHlwZU9mIiwidG9TdHJpbmciLCJwcm9jZXNzIiwiaGFuZGxlIiwibmV4dFRpY2siLCJydW5JZlByZXNlbnQiLCJpbXBvcnRTY3JpcHRzIiwicG9zdE1lc3NhZ2VJc0FzeW5jaHJvbm91cyIsIm9sZE9uTWVzc2FnZSIsIm9ubWVzc2FnZSIsImNhblVzZVBvc3RNZXNzYWdlIiwiTWVzc2FnZUNoYW5uZWwiLCJwb3J0MSIsInBvcnQyIiwiY3JlYXRlRWxlbWVudCIsImRvY3VtZW50RWxlbWVudCIsInNjcmlwdCIsIm9ucmVhZHlzdGF0ZWNoYW5nZSIsInJlbW92ZUNoaWxkIiwiYXBwZW5kQ2hpbGQiLCJyYW5kb20iLCJpbmRleE9mIiwic2xpY2UiLCJhdHRhY2hFdmVudCIsImNhbGxiYWNrIiwiYXJncyIsIkFycmF5IiwidGFzayIsInJ1biIsImNhY2hlZFNldFRpbWVvdXQiLCJjYWNoZWRDbGVhclRpbWVvdXQiLCJkZWZhdWx0U2V0VGltb3V0IiwiZGVmYXVsdENsZWFyVGltZW91dCIsInJ1blRpbWVvdXQiLCJmdW4iLCJjdXJyZW50UXVldWUiLCJxdWV1ZSIsImRyYWluaW5nIiwicXVldWVJbmRleCIsImNsZWFuVXBOZXh0VGljayIsImNvbmNhdCIsImRyYWluUXVldWUiLCJsZW4iLCJydW5DbGVhclRpbWVvdXQiLCJJdGVtIiwiYXJyYXkiLCJub29wIiwidGl0bGUiLCJicm93c2VyIiwiZW52IiwiYXJndiIsInZlcnNpb24iLCJ2ZXJzaW9ucyIsIm9uIiwiYWRkTGlzdGVuZXIiLCJvbmNlIiwib2ZmIiwicmVtb3ZlTGlzdGVuZXIiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJlbWl0IiwicHJlcGVuZExpc3RlbmVyIiwicHJlcGVuZE9uY2VMaXN0ZW5lciIsImxpc3RlbmVycyIsImJpbmRpbmciLCJjd2QiLCJjaGRpciIsImRpciIsInVtYXNrIiwiX3VwZGF0ZSIsImJlZm9yZSIsIl91bm1hcmtBY3RpdmVFbGVtZW50IiwiX2N1cnJlbnQiLCJfbWFya0FjdGl2ZUVsZW1lbnQiLCJjbGFzc05hbWUiLCJyZXBsYWNlIiwiZiIsInJlYWR5U3RhdGUiLCJGVU5DX0VSUk9SX1RFWFQiLCJOQU4iLCJzeW1ib2xUYWciLCJyZVRyaW0iLCJyZUlzQmFkSGV4IiwicmVJc0JpbmFyeSIsInJlSXNPY3RhbCIsImZyZWVQYXJzZUludCIsInBhcnNlSW50IiwiZnJlZUdsb2JhbCIsImZyZWVTZWxmIiwicm9vdCIsIm9iamVjdFRvU3RyaW5nIiwibmF0aXZlTWF4IiwibmF0aXZlTWluIiwibm93IiwiRGF0ZSIsImRlYm91bmNlIiwiZnVuYyIsIndhaXQiLCJvcHRpb25zIiwibGFzdEFyZ3MiLCJsYXN0VGhpcyIsIm1heFdhaXQiLCJyZXN1bHQiLCJ0aW1lcklkIiwibGFzdENhbGxUaW1lIiwibGFzdEludm9rZVRpbWUiLCJsZWFkaW5nIiwibWF4aW5nIiwidHJhaWxpbmciLCJUeXBlRXJyb3IiLCJpbnZva2VGdW5jIiwidGltZSIsInRoaXNBcmciLCJzaG91bGRJbnZva2UiLCJ0aW1lU2luY2VMYXN0Q2FsbCIsInRpbWVyRXhwaXJlZCIsInRyYWlsaW5nRWRnZSIsInJlbWFpbmluZ1dhaXQiLCJkZWJvdW5jZWQiLCJpc0ludm9raW5nIiwibGVhZGluZ0VkZ2UiLCJ0b051bWJlciIsImlzT2JqZWN0IiwiY2FuY2VsIiwiZmx1c2giLCJpc09iamVjdExpa2UiLCJpc1N5bWJvbCIsIm90aGVyIiwidmFsdWVPZiIsImlzQmluYXJ5Il0sIm1hcHBpbmdzIjoiYUFDRSxJQUFJQSxFQUFtQixHQUd2QixTQUFTQyxFQUFvQkMsR0FHNUIsR0FBR0YsRUFBaUJFLEdBQ25CLE9BQU9GLEVBQWlCRSxHQUFVQyxRQUduQyxJQUFJQyxFQUFTSixFQUFpQkUsR0FBWSxDQUN6Q0csRUFBR0gsRUFDSEksR0FBRyxFQUNISCxRQUFTLElBVVYsT0FOQUksRUFBUUwsR0FBVU0sS0FBS0osRUFBT0QsUUFBU0MsRUFBUUEsRUFBT0QsUUFBU0YsR0FHL0RHLEVBQU9FLEdBQUksRUFHSkYsRUFBT0QsUUFLZkYsRUFBb0JRLEVBQUlGLEVBR3hCTixFQUFvQlMsRUFBSVYsRUFHeEJDLEVBQW9CVSxFQUFJLFNBQVNSLEVBQVNTLEVBQU1DLEdBQzNDWixFQUFvQmEsRUFBRVgsRUFBU1MsSUFDbENHLE9BQU9DLGVBQWViLEVBQVNTLEVBQU0sQ0FBRUssWUFBWSxFQUFNQyxJQUFLTCxLQUtoRVosRUFBb0JrQixFQUFJLFNBQVNoQixHQUNYLG9CQUFYaUIsUUFBMEJBLE9BQU9DLGFBQzFDTixPQUFPQyxlQUFlYixFQUFTaUIsT0FBT0MsWUFBYSxDQUFFQyxNQUFPLFdBRTdEUCxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sS0FRdkRyQixFQUFvQnNCLEVBQUksU0FBU0QsRUFBT0UsR0FFdkMsR0FEVSxFQUFQQSxJQUFVRixFQUFRckIsRUFBb0JxQixJQUMvQixFQUFQRSxFQUFVLE9BQU9GLEVBQ3BCLEdBQVcsRUFBUEUsR0FBOEIsaUJBQVZGLEdBQXNCQSxHQUFTQSxFQUFNRyxXQUFZLE9BQU9ILEVBQ2hGLElBQUlJLEVBQUtYLE9BQU9ZLE9BQU8sTUFHdkIsR0FGQTFCLEVBQW9Ca0IsRUFBRU8sR0FDdEJYLE9BQU9DLGVBQWVVLEVBQUksVUFBVyxDQUFFVCxZQUFZLEVBQU1LLE1BQU9BLElB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QUFTaFQsR0FDaEIsR0FBb0IsaUJBQVRBLEVBQ1QsT0FBT0EsRUFFVCxHQWhDRixTQUFrQkEsR0FDaEIsTUFBdUIsaUJBQVRBLEdBdEJoQixTQUFzQkEsR0FDcEIsUUFBU0EsR0FBeUIsaUJBQVRBLEVBc0J0Qm9ULENBQWFwVCxJQUFVaVIsRUFBZS9SLEtBQUtjLElBQVV1USxFQThCcEQ4QyxDQUFTclQsR0FDWCxPQUFPc1EsRUFFVCxHQUFJMkMsRUFBU2pULEdBQVEsQ0FDbkIsSUFBSXNULEVBQWdDLG1CQUFqQnRULEVBQU11VCxRQUF3QnZULEVBQU11VCxVQUFZdlQsRUFDbkVBLEVBQVFpVCxFQUFTSyxHQUFVQSxFQUFRLEdBQU1BLEVBRTNDLEdBQW9CLGlCQUFUdFQsRUFDVCxPQUFpQixJQUFWQSxFQUFjQSxHQUFTQSxFQUVoQ0EsRUFBUUEsRUFBTWtRLFFBQVFNLEVBQVEsSUFDOUIsSUFBSWdELEVBQVc5QyxFQUFXekgsS0FBS2pKLEdBQy9CLE9BQVF3VCxHQUFZN0MsRUFBVTFILEtBQUtqSixHQUMvQjRRLEVBQWE1USxFQUFNOE0sTUFBTSxHQUFJMEcsRUFBVyxFQUFJLEdBQzNDL0MsRUFBV3hILEtBQUtqSixHQUFTc1EsR0FBT3RRLEVBR3ZDbEIsRUFBT0QsUUE5SVAsU0FBa0IwUyxFQUFNQyxFQUFNQyxHQUM1QixJQUFJUSxHQUFVLEVBQ1ZFLEdBQVcsRUFFZixHQUFtQixtQkFBUlosRUFDVCxNQUFNLElBQUlhLFVBQVUvQixHQU10QixPQUpJNEMsRUFBU3hCLEtBQ1hRLEVBQVUsWUFBYVIsSUFBWUEsRUFBUVEsUUFBVUEsRUFDckRFLEVBQVcsYUFBY1YsSUFBWUEsRUFBUVUsU0FBV0EsR0FFbkRiLEVBQVNDLEVBQU1DLEVBQU0sQ0FDMUIsUUFBV1MsRUFDWCxRQUFXVCxFQUNYLFNBQVlXLE8iLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMyk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwidmFyIGc7XG5cbi8vIFRoaXMgd29ya3MgaW4gbm9uLXN0cmljdCBtb2RlXG5nID0gKGZ1bmN0aW9uKCkge1xuXHRyZXR1cm4gdGhpcztcbn0pKCk7XG5cbnRyeSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgZXZhbCBpcyBhbGxvd2VkIChzZWUgQ1NQKVxuXHRnID0gZyB8fCBuZXcgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpO1xufSBjYXRjaCAoZSkge1xuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxuXHRpZiAodHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIikgZyA9IHdpbmRvdztcbn1cblxuLy8gZyBjYW4gc3RpbGwgYmUgdW5kZWZpbmVkLCBidXQgbm90aGluZyB0byBkbyBhYm91dCBpdC4uLlxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3Ncbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cblxubW9kdWxlLmV4cG9ydHMgPSBnO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IGNvZGVMaW5lQ2xhc3MgPSAnY29kZS1saW5lJztcbmZ1bmN0aW9uIGNsYW1wKG1pbiwgbWF4LCB2YWx1ZSkge1xuICAgIHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lKSB7XG4gICAgcmV0dXJuIGNsYW1wKDAsIHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuICAgIGxldCBlbGVtZW50cztcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBpZiAoIWVsZW1lbnRzKSB7XG4gICAgICAgICAgICBlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShjb2RlTGluZUNsYXNzKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuICAgICAgICAgICAgICAgIGlmIChpc05hTihsaW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQudGFnTmFtZSA9PT0gJ0NPREUnICYmIGVsZW1lbnQucGFyZW50RWxlbWVudCAmJiBlbGVtZW50LnBhcmVudEVsZW1lbnQudGFnTmFtZSA9PT0gJ1BSRScpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gRmVuY2hlZCBjb2RlIGJsb2NrcyBhcmUgYSBzcGVjaWFsIGNhc2Ugc2luY2UgdGhlIGBjb2RlLWxpbmVgIGNhbiBvbmx5IGJlIG1hcmtlZCBvblxuICAgICAgICAgICAgICAgICAgICAvLyB0aGUgYDxjb2RlPmAgZWxlbWVudCBhbmQgbm90IHRoZSBwYXJlbnQgYDxwcmU+YCBlbGVtZW50LlxuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudC5wYXJlbnRFbGVtZW50LCBsaW5lIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQsIGxpbmUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBlbGVtZW50cztcbiAgICB9O1xufSkoKTtcbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5mdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZSkge1xuICAgIGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuICAgIGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuICAgIGxldCBwcmV2aW91cyA9IGxpbmVzWzBdIHx8IG51bGw7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuICAgICAgICBpZiAoZW50cnkubGluZSA9PT0gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGVudHJ5LCBuZXh0OiB1bmRlZmluZWQgfTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuICAgICAgICAgICAgcmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG4gICAgICAgIH1cbiAgICAgICAgcHJldmlvdXMgPSBlbnRyeTtcbiAgICB9XG4gICAgcmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cbmV4cG9ydHMuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5mdW5jdGlvbiBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgY29uc3QgcG9zaXRpb24gPSBvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWTtcbiAgICBsZXQgbG8gPSAtMTtcbiAgICBsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuICAgIHdoaWxlIChsbyArIDEgPCBoaSkge1xuICAgICAgICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuICAgICAgICBjb25zdCBib3VuZHMgPSBnZXRFbGVtZW50Qm91bmRzKGxpbmVzW21pZF0pO1xuICAgICAgICBpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcbiAgICAgICAgICAgIGhpID0gbWlkO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgbG8gPSBtaWQ7XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuICAgIGNvbnN0IGhpQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhoaUVsZW1lbnQpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIGlmIChoaSA+IDEgJiYgaGkgPCBsaW5lcy5sZW5ndGggJiYgaGlCb3VuZHMudG9wICsgaGlCb3VuZHMuaGVpZ2h0ID4gcG9zaXRpb24pIHtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCwgbmV4dDogbGluZXNbaGkgKyAxXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldDtcbmZ1bmN0aW9uIGdldEVsZW1lbnRCb3VuZHMoeyBlbGVtZW50IH0pIHtcbiAgICBjb25zdCBteUJvdW5kcyA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgLy8gU29tZSBjb2RlIGxpbmUgZWxlbWVudHMgbWF5IGNvbnRhaW4gb3RoZXIgY29kZSBsaW5lIGVsZW1lbnRzLlxuICAgIC8vIEluIHRob3NlIGNhc2VzLCBvbmx5IHRha2UgdGhlIGhlaWdodCB1cCB0byB0aGF0IGNoaWxkLlxuICAgIGNvbnN0IGNvZGVMaW5lQ2hpbGQgPSBlbGVtZW50LnF1ZXJ5U2VsZWN0b3IoYC4ke2NvZGVMaW5lQ2xhc3N9YCk7XG4gICAgaWYgKGNvZGVMaW5lQ2hpbGQpIHtcbiAgICAgICAgY29uc3QgY2hpbGRCb3VuZHMgPSBjb2RlTGluZUNoaWxkLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBoZWlnaHQgPSBNYXRoLm1heCgxLCAoY2hpbGRCb3VuZHMudG9wIC0gbXlCb3VuZHMudG9wKSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0b3A6IG15Qm91bmRzLnRvcCxcbiAgICAgICAgICAgIGhlaWdodDogaGVpZ2h0XG4gICAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiBteUJvdW5kcztcbn1cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmUpIHtcbiAgICBpZiAoIXNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChsaW5lIDw9IDApIHtcbiAgICAgICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgIGlmICghcHJldmlvdXMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgc2Nyb2xsVG8gPSAwO1xuICAgIGNvbnN0IHJlY3QgPSBnZXRFbGVtZW50Qm91bmRzKHByZXZpb3VzKTtcbiAgICBjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuICAgIGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuICAgICAgICAvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuICAgICAgICBjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICBjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuICAgICAgICBzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICAgIGNvbnN0IHByb2dyZXNzSW5FbGVtZW50ID0gbGluZSAtIE1hdGguZmxvb3IobGluZSk7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG4gICAgfVxuICAgIHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cbmV4cG9ydHMuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lID0gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lO1xuZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KSB7XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG4gICAgaWYgKHByZXZpb3VzKSB7XG4gICAgICAgIGNvbnN0IHByZXZpb3VzQm91bmRzID0gZ2V0RWxlbWVudEJvdW5kcyhwcmV2aW91cyk7XG4gICAgICAgIGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICBjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChnZXRFbGVtZW50Qm91bmRzKG5leHQpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuZXhwb3J0cy5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0O1xuLyoqXG4gKiBUcnkgdG8gZmluZCB0aGUgaHRtbCBlbGVtZW50IGJ5IHVzaW5nIGEgZnJhZ21lbnQgaWRcbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudChmcmFnbWVudCkge1xuICAgIHJldHVybiBnZXRDb2RlTGluZUVsZW1lbnRzKCkuZmluZCgoZWxlbWVudCkgPT4ge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcbiAgICB9KTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbmNvbnN0IHN0YXRlID0geyAuLi52c2NvZGUuZ2V0U3RhdGUoKSwgLi4uc2V0dGluZ3NfMS5nZXREYXRhKCdkYXRhLXN0YXRlJykgfTtcbi8vIE1ha2Ugc3VyZSB0byBzeW5jIFZTIENvZGUgc3RhdGUgaGVyZVxudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbmNvbnN0IG1lc3NhZ2luZyA9IG1lc3NhZ2luZ18xLmNyZWF0ZVBvc3RlckZvclZzQ29kZSh2c2NvZGUpO1xud2luZG93LmNzcEFsZXJ0ZXIuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvci5zZXRQb3N0ZXIobWVzc2FnaW5nKTtcbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufTtcbmV2ZW50c18xLm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG4gICAgY29uc3Qgc2Nyb2xsUHJvZ3Jlc3MgPSBzdGF0ZS5zY3JvbGxQcm9ncmVzcztcbiAgICBpZiAodHlwZW9mIHNjcm9sbFByb2dyZXNzID09PSAnbnVtYmVyJyAmJiAhc2V0dGluZ3MuZnJhZ21lbnQpIHtcbiAgICAgICAgc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICAgICAgICAgIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgICAgIHdpbmRvdy5zY3JvbGxUbygwLCBzY3JvbGxQcm9ncmVzcyAqIGRvY3VtZW50LmJvZHkuY2xpZW50SGVpZ2h0KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHNldHRpbmdzLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG4gICAgICAgIHNldEltbWVkaWF0ZSgoKSA9PiB7XG4gICAgICAgICAgICAvLyBUcnkgdG8gc2Nyb2xsIHRvIGZyYWdtZW50IGlmIGF2YWlsYWJsZVxuICAgICAgICAgICAgaWYgKHNldHRpbmdzLmZyYWdtZW50KSB7XG4gICAgICAgICAgICAgICAgc3RhdGUuZnJhZ21lbnQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgdnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gc2Nyb2xsX3N5bmNfMS5nZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KHNldHRpbmdzLmZyYWdtZW50KTtcbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGVsZW1lbnQubGluZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc05hTihzZXR0aW5ncy5saW5lKSkge1xuICAgICAgICAgICAgICAgICAgICBzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbF9zeW5jXzEuc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKHNldHRpbmdzLmxpbmUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxufSk7XG5jb25zdCBvblVwZGF0ZVZpZXcgPSAoKCkgPT4ge1xuICAgIGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmUpID0+IHtcbiAgICAgICAgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICBzY3JvbGxfc3luY18xLnNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKTtcbiAgICB9LCA1MCk7XG4gICAgcmV0dXJuIChsaW5lKSA9PiB7XG4gICAgICAgIGlmICghaXNOYU4obGluZSkpIHtcbiAgICAgICAgICAgIHN0YXRlLmxpbmUgPSBsaW5lO1xuICAgICAgICAgICAgZG9TY3JvbGwobGluZSk7XG4gICAgICAgIH1cbiAgICB9O1xufSkoKTtcbmxldCB1cGRhdGVJbWFnZVNpemVzID0gdGhyb3R0bGUoKCkgPT4ge1xuICAgIGNvbnN0IGltYWdlSW5mbyA9IFtdO1xuICAgIGxldCBpbWFnZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW1nJyk7XG4gICAgaWYgKGltYWdlcykge1xuICAgICAgICBsZXQgaTtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGltYWdlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29uc3QgaW1nID0gaW1hZ2VzW2ldO1xuICAgICAgICAgICAgaWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuICAgICAgICAgICAgICAgIGltZy5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpbWFnZUluZm8ucHVzaCh7XG4gICAgICAgICAgICAgICAgaWQ6IGltZy5pZCxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IGltZy5oZWlnaHQsXG4gICAgICAgICAgICAgICAgd2lkdGg6IGltZy53aWR0aFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgbWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuICAgIH1cbn0sIDUwKTtcbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG4gICAgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuICAgIHVwZGF0ZVNjcm9sbFByb2dyZXNzKCk7XG4gICAgdXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIGV2ZW50ID0+IHtcbiAgICBpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG4gICAgICAgIGNhc2UgJ29uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbic6XG4gICAgICAgICAgICBtYXJrZXIub25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGV2ZW50LmRhdGEubGluZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAndXBkYXRlVmlldyc6XG4gICAgICAgICAgICBvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgIH1cbn0sIGZhbHNlKTtcbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZXZlbnQgPT4ge1xuICAgIGlmICghc2V0dGluZ3MuZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gSWdub3JlIGNsaWNrcyBvbiBsaW5rc1xuICAgIGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSA9PT0gJ0EnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICB9XG4gICAgY29uc3Qgb2Zmc2V0ID0gZXZlbnQucGFnZVk7XG4gICAgY29uc3QgbGluZSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KTtcbiAgICBpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2RpZENsaWNrJywgeyBsaW5lOiBNYXRoLmZsb29yKGxpbmUpIH0pO1xuICAgIH1cbn0pO1xuY29uc3QgcGFzc1Rocm91Z2hMaW5rU2NoZW1lcyA9IFsnaHR0cDonLCAnaHR0cHM6JywgJ21haWx0bzonLCAndnNjb2RlOicsICd2c2NvZGUtaW5zaWRlcnM6J107XG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGV2ZW50ID0+IHtcbiAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IG5vZGUgPSBldmVudC50YXJnZXQ7XG4gICAgd2hpbGUgKG5vZGUpIHtcbiAgICAgICAgaWYgKG5vZGUudGFnTmFtZSAmJiBub2RlLnRhZ05hbWUgPT09ICdBJyAmJiBub2RlLmhyZWYpIHtcbiAgICAgICAgICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSgnaHJlZicpLnN0YXJ0c1dpdGgoJyMnKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFBhc3MgdGhyb3VnaCBrbm93biBzY2hlbWVzXG4gICAgICAgICAgICBpZiAocGFzc1Rocm91Z2hMaW5rU2NoZW1lcy5zb21lKHNjaGVtZSA9PiBub2RlLmhyZWYuc3RhcnRzV2l0aChzY2hlbWUpKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGhyZWZUZXh0ID0gbm9kZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtaHJlZicpIHx8IG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJyk7XG4gICAgICAgICAgICAvLyBJZiBvcmlnaW5hbCBsaW5rIGRvZXNuJ3QgbG9vayBsaWtlIGEgdXJsLCBkZWxlZ2F0ZSBiYWNrIHRvIFZTIENvZGUgdG8gcmVzb2x2ZVxuICAgICAgICAgICAgaWYgKCEvXlthLXpcXC1dKzovaS50ZXN0KGhyZWZUZXh0KSkge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnb3BlbkxpbmsnLCB7IGhyZWY6IGhyZWZUZXh0IH0pO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG4gICAgfVxufSwgdHJ1ZSk7XG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgdGhyb3R0bGUoKCkgPT4ge1xuICAgIHVwZGF0ZVNjcm9sbFByb2dyZXNzKCk7XG4gICAgaWYgKHNjcm9sbERpc2FibGVkKSB7XG4gICAgICAgIHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgICBjb25zdCBsaW5lID0gc2Nyb2xsX3N5bmNfMS5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG4gICAgICAgIGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG4gICAgICAgICAgICBtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG4gICAgICAgIH1cbiAgICB9XG59LCA1MCkpO1xuZnVuY3Rpb24gdXBkYXRlU2Nyb2xsUHJvZ3Jlc3MoKSB7XG4gICAgc3RhdGUuc2Nyb2xsUHJvZ3Jlc3MgPSB3aW5kb3cuc2Nyb2xsWSAvIGRvY3VtZW50LmJvZHkuY2xpZW50SGVpZ2h0O1xuICAgIHZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG59XG4iLCJ2YXIgYXBwbHkgPSBGdW5jdGlvbi5wcm90b3R5cGUuYXBwbHk7XG5cbi8vIERPTSBBUElzLCBmb3IgY29tcGxldGVuZXNzXG5cbmV4cG9ydHMuc2V0VGltZW91dCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRUaW1lb3V0LCB3aW5kb3csIGFyZ3VtZW50cyksIGNsZWFyVGltZW91dCk7XG59O1xuZXhwb3J0cy5zZXRJbnRlcnZhbCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gbmV3IFRpbWVvdXQoYXBwbHkuY2FsbChzZXRJbnRlcnZhbCwgd2luZG93LCBhcmd1bWVudHMpLCBjbGVhckludGVydmFsKTtcbn07XG5leHBvcnRzLmNsZWFyVGltZW91dCA9XG5leHBvcnRzLmNsZWFySW50ZXJ2YWwgPSBmdW5jdGlvbih0aW1lb3V0KSB7XG4gIGlmICh0aW1lb3V0KSB7XG4gICAgdGltZW91dC5jbG9zZSgpO1xuICB9XG59O1xuXG5mdW5jdGlvbiBUaW1lb3V0KGlkLCBjbGVhckZuKSB7XG4gIHRoaXMuX2lkID0gaWQ7XG4gIHRoaXMuX2NsZWFyRm4gPSBjbGVhckZuO1xufVxuVGltZW91dC5wcm90b3R5cGUudW5yZWYgPSBUaW1lb3V0LnByb3RvdHlwZS5yZWYgPSBmdW5jdGlvbigpIHt9O1xuVGltZW91dC5wcm90b3R5cGUuY2xvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5fY2xlYXJGbi5jYWxsKHdpbmRvdywgdGhpcy5faWQpO1xufTtcblxuLy8gRG9lcyBub3Qgc3RhcnQgdGhlIHRpbWUsIGp1c3Qgc2V0cyB1cCB0aGUgbWVtYmVycyBuZWVkZWQuXG5leHBvcnRzLmVucm9sbCA9IGZ1bmN0aW9uKGl0ZW0sIG1zZWNzKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcbiAgaXRlbS5faWRsZVRpbWVvdXQgPSBtc2Vjcztcbn07XG5cbmV4cG9ydHMudW5lbnJvbGwgPSBmdW5jdGlvbihpdGVtKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcbiAgaXRlbS5faWRsZVRpbWVvdXQgPSAtMTtcbn07XG5cbmV4cG9ydHMuX3VucmVmQWN0aXZlID0gZXhwb3J0cy5hY3RpdmUgPSBmdW5jdGlvbihpdGVtKSB7XG4gIGNsZWFyVGltZW91dChpdGVtLl9pZGxlVGltZW91dElkKTtcblxuICB2YXIgbXNlY3MgPSBpdGVtLl9pZGxlVGltZW91dDtcbiAgaWYgKG1zZWNzID49IDApIHtcbiAgICBpdGVtLl9pZGxlVGltZW91dElkID0gc2V0VGltZW91dChmdW5jdGlvbiBvblRpbWVvdXQoKSB7XG4gICAgICBpZiAoaXRlbS5fb25UaW1lb3V0KVxuICAgICAgICBpdGVtLl9vblRpbWVvdXQoKTtcbiAgICB9LCBtc2Vjcyk7XG4gIH1cbn07XG5cbi8vIHNldGltbWVkaWF0ZSBhdHRhY2hlcyBpdHNlbGYgdG8gdGhlIGdsb2JhbCBvYmplY3RcbnJlcXVpcmUoXCJzZXRpbW1lZGlhdGVcIik7XG4vLyBPbiBzb21lIGV4b3RpYyBlbnZpcm9ubWVudHMsIGl0J3Mgbm90IGNsZWFyIHdoaWNoIG9iamVjdCBgc2V0aW1tZWlkYXRlYCB3YXNcbi8vIGFibGUgdG8gaW5zdGFsbCBvbnRvLiAgU2VhcmNoIGVhY2ggcG9zc2liaWxpdHkgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlXG4vLyBgc2V0aW1tZWRpYXRlYCBsaWJyYXJ5LlxuZXhwb3J0cy5zZXRJbW1lZGlhdGUgPSAodHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIgJiYgc2VsZi5zZXRJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICh0eXBlb2YgZ2xvYmFsICE9PSBcInVuZGVmaW5lZFwiICYmIGdsb2JhbC5zZXRJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICh0aGlzICYmIHRoaXMuc2V0SW1tZWRpYXRlKTtcbmV4cG9ydHMuY2xlYXJJbW1lZGlhdGUgPSAodHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIgJiYgc2VsZi5jbGVhckltbWVkaWF0ZSkgfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAodHlwZW9mIGdsb2JhbCAhPT0gXCJ1bmRlZmluZWRcIiAmJiBnbG9iYWwuY2xlYXJJbW1lZGlhdGUpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgICAgKHRoaXMgJiYgdGhpcy5jbGVhckltbWVkaWF0ZSk7XG4iLCIoZnVuY3Rpb24gKGdsb2JhbCwgdW5kZWZpbmVkKSB7XG4gICAgXCJ1c2Ugc3RyaWN0XCI7XG5cbiAgICBpZiAoZ2xvYmFsLnNldEltbWVkaWF0ZSkge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIG5leHRIYW5kbGUgPSAxOyAvLyBTcGVjIHNheXMgZ3JlYXRlciB0aGFuIHplcm9cbiAgICB2YXIgdGFza3NCeUhhbmRsZSA9IHt9O1xuICAgIHZhciBjdXJyZW50bHlSdW5uaW5nQVRhc2sgPSBmYWxzZTtcbiAgICB2YXIgZG9jID0gZ2xvYmFsLmRvY3VtZW50O1xuICAgIHZhciByZWdpc3RlckltbWVkaWF0ZTtcblxuICAgIGZ1bmN0aW9uIHNldEltbWVkaWF0ZShjYWxsYmFjaykge1xuICAgICAgLy8gQ2FsbGJhY2sgY2FuIGVpdGhlciBiZSBhIGZ1bmN0aW9uIG9yIGEgc3RyaW5nXG4gICAgICBpZiAodHlwZW9mIGNhbGxiYWNrICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY2FsbGJhY2sgPSBuZXcgRnVuY3Rpb24oXCJcIiArIGNhbGxiYWNrKTtcbiAgICAgIH1cbiAgICAgIC8vIENvcHkgZnVuY3Rpb24gYXJndW1lbnRzXG4gICAgICB2YXIgYXJncyA9IG5ldyBBcnJheShhcmd1bWVudHMubGVuZ3RoIC0gMSk7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBhcmdzW2ldID0gYXJndW1lbnRzW2kgKyAxXTtcbiAgICAgIH1cbiAgICAgIC8vIFN0b3JlIGFuZCByZWdpc3RlciB0aGUgdGFza1xuICAgICAgdmFyIHRhc2sgPSB7IGNhbGxiYWNrOiBjYWxsYmFjaywgYXJnczogYXJncyB9O1xuICAgICAgdGFza3NCeUhhbmRsZVtuZXh0SGFuZGxlXSA9IHRhc2s7XG4gICAgICByZWdpc3RlckltbWVkaWF0ZShuZXh0SGFuZGxlKTtcbiAgICAgIHJldHVybiBuZXh0SGFuZGxlKys7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gY2xlYXJJbW1lZGlhdGUoaGFuZGxlKSB7XG4gICAgICAgIGRlbGV0ZSB0YXNrc0J5SGFuZGxlW2hhbmRsZV07XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcnVuKHRhc2spIHtcbiAgICAgICAgdmFyIGNhbGxiYWNrID0gdGFzay5jYWxsYmFjaztcbiAgICAgICAgdmFyIGFyZ3MgPSB0YXNrLmFyZ3M7XG4gICAgICAgIHN3aXRjaCAoYXJncy5sZW5ndGgpIHtcbiAgICAgICAgY2FzZSAwOlxuICAgICAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDI6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdLCBhcmdzWzFdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgICBjYWxsYmFjayhhcmdzWzBdLCBhcmdzWzFdLCBhcmdzWzJdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgY2FsbGJhY2suYXBwbHkodW5kZWZpbmVkLCBhcmdzKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gcnVuSWZQcmVzZW50KGhhbmRsZSkge1xuICAgICAgICAvLyBGcm9tIHRoZSBzcGVjOiBcIldhaXQgdW50aWwgYW55IGludm9jYXRpb25zIG9mIHRoaXMgYWxnb3JpdGhtIHN0YXJ0ZWQgYmVmb3JlIHRoaXMgb25lIGhhdmUgY29tcGxldGVkLlwiXG4gICAgICAgIC8vIFNvIGlmIHdlJ3JlIGN1cnJlbnRseSBydW5uaW5nIGEgdGFzaywgd2UnbGwgbmVlZCB0byBkZWxheSB0aGlzIGludm9jYXRpb24uXG4gICAgICAgIGlmIChjdXJyZW50bHlSdW5uaW5nQVRhc2spIHtcbiAgICAgICAgICAgIC8vIERlbGF5IGJ5IGRvaW5nIGEgc2V0VGltZW91dC4gc2V0SW1tZWRpYXRlIHdhcyB0cmllZCBpbnN0ZWFkLCBidXQgaW4gRmlyZWZveCA3IGl0IGdlbmVyYXRlZCBhXG4gICAgICAgICAgICAvLyBcInRvbyBtdWNoIHJlY3Vyc2lvblwiIGVycm9yLlxuICAgICAgICAgICAgc2V0VGltZW91dChydW5JZlByZXNlbnQsIDAsIGhhbmRsZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB2YXIgdGFzayA9IHRhc2tzQnlIYW5kbGVbaGFuZGxlXTtcbiAgICAgICAgICAgIGlmICh0YXNrKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudGx5UnVubmluZ0FUYXNrID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBydW4odGFzayk7XG4gICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJJbW1lZGlhdGUoaGFuZGxlKTtcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudGx5UnVubmluZ0FUYXNrID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaW5zdGFsbE5leHRUaWNrSW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHJlZ2lzdGVySW1tZWRpYXRlID0gZnVuY3Rpb24oaGFuZGxlKSB7XG4gICAgICAgICAgICBwcm9jZXNzLm5leHRUaWNrKGZ1bmN0aW9uICgpIHsgcnVuSWZQcmVzZW50KGhhbmRsZSk7IH0pO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNhblVzZVBvc3RNZXNzYWdlKCkge1xuICAgICAgICAvLyBUaGUgdGVzdCBhZ2FpbnN0IGBpbXBvcnRTY3JpcHRzYCBwcmV2ZW50cyB0aGlzIGltcGxlbWVudGF0aW9uIGZyb20gYmVpbmcgaW5zdGFsbGVkIGluc2lkZSBhIHdlYiB3b3JrZXIsXG4gICAgICAgIC8vIHdoZXJlIGBnbG9iYWwucG9zdE1lc3NhZ2VgIG1lYW5zIHNvbWV0aGluZyBjb21wbGV0ZWx5IGRpZmZlcmVudCBhbmQgY2FuJ3QgYmUgdXNlZCBmb3IgdGhpcyBwdXJwb3NlLlxuICAgICAgICBpZiAoZ2xvYmFsLnBvc3RNZXNzYWdlICYmICFnbG9iYWwuaW1wb3J0U2NyaXB0cykge1xuICAgICAgICAgICAgdmFyIHBvc3RNZXNzYWdlSXNBc3luY2hyb25vdXMgPSB0cnVlO1xuICAgICAgICAgICAgdmFyIG9sZE9uTWVzc2FnZSA9IGdsb2JhbC5vbm1lc3NhZ2U7XG4gICAgICAgICAgICBnbG9iYWwub25tZXNzYWdlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgICAgcG9zdE1lc3NhZ2VJc0FzeW5jaHJvbm91cyA9IGZhbHNlO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGdsb2JhbC5wb3N0TWVzc2FnZShcIlwiLCBcIipcIik7XG4gICAgICAgICAgICBnbG9iYWwub25tZXNzYWdlID0gb2xkT25NZXNzYWdlO1xuICAgICAgICAgICAgcmV0dXJuIHBvc3RNZXNzYWdlSXNBc3luY2hyb25vdXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpbnN0YWxsUG9zdE1lc3NhZ2VJbXBsZW1lbnRhdGlvbigpIHtcbiAgICAgICAgLy8gSW5zdGFsbHMgYW4gZXZlbnQgaGFuZGxlciBvbiBgZ2xvYmFsYCBmb3IgdGhlIGBtZXNzYWdlYCBldmVudDogc2VlXG4gICAgICAgIC8vICogaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4vRE9NL3dpbmRvdy5wb3N0TWVzc2FnZVxuICAgICAgICAvLyAqIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL2NvbW1zLmh0bWwjY3Jvc3NEb2N1bWVudE1lc3NhZ2VzXG5cbiAgICAgICAgdmFyIG1lc3NhZ2VQcmVmaXggPSBcInNldEltbWVkaWF0ZSRcIiArIE1hdGgucmFuZG9tKCkgKyBcIiRcIjtcbiAgICAgICAgdmFyIG9uR2xvYmFsTWVzc2FnZSA9IGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgICAgICBpZiAoZXZlbnQuc291cmNlID09PSBnbG9iYWwgJiZcbiAgICAgICAgICAgICAgICB0eXBlb2YgZXZlbnQuZGF0YSA9PT0gXCJzdHJpbmdcIiAmJlxuICAgICAgICAgICAgICAgIGV2ZW50LmRhdGEuaW5kZXhPZihtZXNzYWdlUHJlZml4KSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHJ1bklmUHJlc2VudCgrZXZlbnQuZGF0YS5zbGljZShtZXNzYWdlUHJlZml4Lmxlbmd0aCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuXG4gICAgICAgIGlmIChnbG9iYWwuYWRkRXZlbnRMaXN0ZW5lcikge1xuICAgICAgICAgICAgZ2xvYmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIG9uR2xvYmFsTWVzc2FnZSwgZmFsc2UpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZ2xvYmFsLmF0dGFjaEV2ZW50KFwib25tZXNzYWdlXCIsIG9uR2xvYmFsTWVzc2FnZSk7XG4gICAgICAgIH1cblxuICAgICAgICByZWdpc3RlckltbWVkaWF0ZSA9IGZ1bmN0aW9uKGhhbmRsZSkge1xuICAgICAgICAgICAgZ2xvYmFsLnBvc3RNZXNzYWdlKG1lc3NhZ2VQcmVmaXggKyBoYW5kbGUsIFwiKlwiKTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBpbnN0YWxsTWVzc2FnZUNoYW5uZWxJbXBsZW1lbnRhdGlvbigpIHtcbiAgICAgICAgdmFyIGNoYW5uZWwgPSBuZXcgTWVzc2FnZUNoYW5uZWwoKTtcbiAgICAgICAgY2hhbm5lbC5wb3J0MS5vbm1lc3NhZ2UgPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgICAgdmFyIGhhbmRsZSA9IGV2ZW50LmRhdGE7XG4gICAgICAgICAgICBydW5JZlByZXNlbnQoaGFuZGxlKTtcbiAgICAgICAgfTtcblxuICAgICAgICByZWdpc3RlckltbWVkaWF0ZSA9IGZ1bmN0aW9uKGhhbmRsZSkge1xuICAgICAgICAgICAgY2hhbm5lbC5wb3J0Mi5wb3N0TWVzc2FnZShoYW5kbGUpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc3RhbGxSZWFkeVN0YXRlQ2hhbmdlSW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHZhciBodG1sID0gZG9jLmRvY3VtZW50RWxlbWVudDtcbiAgICAgICAgcmVnaXN0ZXJJbW1lZGlhdGUgPSBmdW5jdGlvbihoYW5kbGUpIHtcbiAgICAgICAgICAgIC8vIENyZWF0ZSBhIDxzY3JpcHQ+IGVsZW1lbnQ7IGl0cyByZWFkeXN0YXRlY2hhbmdlIGV2ZW50IHdpbGwgYmUgZmlyZWQgYXN5bmNocm9ub3VzbHkgb25jZSBpdCBpcyBpbnNlcnRlZFxuICAgICAgICAgICAgLy8gaW50byB0aGUgZG9jdW1lbnQuIERvIHNvLCB0aHVzIHF1ZXVpbmcgdXAgdGhlIHRhc2suIFJlbWVtYmVyIHRvIGNsZWFuIHVwIG9uY2UgaXQncyBiZWVuIGNhbGxlZC5cbiAgICAgICAgICAgIHZhciBzY3JpcHQgPSBkb2MuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKTtcbiAgICAgICAgICAgIHNjcmlwdC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcnVuSWZQcmVzZW50KGhhbmRsZSk7XG4gICAgICAgICAgICAgICAgc2NyaXB0Lm9ucmVhZHlzdGF0ZWNoYW5nZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgaHRtbC5yZW1vdmVDaGlsZChzY3JpcHQpO1xuICAgICAgICAgICAgICAgIHNjcmlwdCA9IG51bGw7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgaHRtbC5hcHBlbmRDaGlsZChzY3JpcHQpO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGluc3RhbGxTZXRUaW1lb3V0SW1wbGVtZW50YXRpb24oKSB7XG4gICAgICAgIHJlZ2lzdGVySW1tZWRpYXRlID0gZnVuY3Rpb24oaGFuZGxlKSB7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KHJ1bklmUHJlc2VudCwgMCwgaGFuZGxlKTtcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBJZiBzdXBwb3J0ZWQsIHdlIHNob3VsZCBhdHRhY2ggdG8gdGhlIHByb3RvdHlwZSBvZiBnbG9iYWwsIHNpbmNlIHRoYXQgaXMgd2hlcmUgc2V0VGltZW91dCBldCBhbC4gbGl2ZS5cbiAgICB2YXIgYXR0YWNoVG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YgJiYgT2JqZWN0LmdldFByb3RvdHlwZU9mKGdsb2JhbCk7XG4gICAgYXR0YWNoVG8gPSBhdHRhY2hUbyAmJiBhdHRhY2hUby5zZXRUaW1lb3V0ID8gYXR0YWNoVG8gOiBnbG9iYWw7XG5cbiAgICAvLyBEb24ndCBnZXQgZm9vbGVkIGJ5IGUuZy4gYnJvd3NlcmlmeSBlbnZpcm9ubWVudHMuXG4gICAgaWYgKHt9LnRvU3RyaW5nLmNhbGwoZ2xvYmFsLnByb2Nlc3MpID09PSBcIltvYmplY3QgcHJvY2Vzc11cIikge1xuICAgICAgICAvLyBGb3IgTm9kZS5qcyBiZWZvcmUgMC45XG4gICAgICAgIGluc3RhbGxOZXh0VGlja0ltcGxlbWVudGF0aW9uKCk7XG5cbiAgICB9IGVsc2UgaWYgKGNhblVzZVBvc3RNZXNzYWdlKCkpIHtcbiAgICAgICAgLy8gRm9yIG5vbi1JRTEwIG1vZGVybiBicm93c2Vyc1xuICAgICAgICBpbnN0YWxsUG9zdE1lc3NhZ2VJbXBsZW1lbnRhdGlvbigpO1xuXG4gICAgfSBlbHNlIGlmIChnbG9iYWwuTWVzc2FnZUNoYW5uZWwpIHtcbiAgICAgICAgLy8gRm9yIHdlYiB3b3JrZXJzLCB3aGVyZSBzdXBwb3J0ZWRcbiAgICAgICAgaW5zdGFsbE1lc3NhZ2VDaGFubmVsSW1wbGVtZW50YXRpb24oKTtcblxuICAgIH0gZWxzZSBpZiAoZG9jICYmIFwib25yZWFkeXN0YXRlY2hhbmdlXCIgaW4gZG9jLmNyZWF0ZUVsZW1lbnQoXCJzY3JpcHRcIikpIHtcbiAgICAgICAgLy8gRm9yIElFIDbigJM4XG4gICAgICAgIGluc3RhbGxSZWFkeVN0YXRlQ2hhbmdlSW1wbGVtZW50YXRpb24oKTtcblxuICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEZvciBvbGRlciBicm93c2Vyc1xuICAgICAgICBpbnN0YWxsU2V0VGltZW91dEltcGxlbWVudGF0aW9uKCk7XG4gICAgfVxuXG4gICAgYXR0YWNoVG8uc2V0SW1tZWRpYXRlID0gc2V0SW1tZWRpYXRlO1xuICAgIGF0dGFjaFRvLmNsZWFySW1tZWRpYXRlID0gY2xlYXJJbW1lZGlhdGU7XG59KHR5cGVvZiBzZWxmID09PSBcInVuZGVmaW5lZFwiID8gdHlwZW9mIGdsb2JhbCA9PT0gXCJ1bmRlZmluZWRcIiA/IHRoaXMgOiBnbG9iYWwgOiBzZWxmKSk7XG4iLCIvLyBzaGltIGZvciB1c2luZyBwcm9jZXNzIGluIGJyb3dzZXJcbnZhciBwcm9jZXNzID0gbW9kdWxlLmV4cG9ydHMgPSB7fTtcblxuLy8gY2FjaGVkIGZyb20gd2hhdGV2ZXIgZ2xvYmFsIGlzIHByZXNlbnQgc28gdGhhdCB0ZXN0IHJ1bm5lcnMgdGhhdCBzdHViIGl0XG4vLyBkb24ndCBicmVhayB0aGluZ3MuICBCdXQgd2UgbmVlZCB0byB3cmFwIGl0IGluIGEgdHJ5IGNhdGNoIGluIGNhc2UgaXQgaXNcbi8vIHdyYXBwZWQgaW4gc3RyaWN0IG1vZGUgY29kZSB3aGljaCBkb2Vzbid0IGRlZmluZSBhbnkgZ2xvYmFscy4gIEl0J3MgaW5zaWRlIGFcbi8vIGZ1bmN0aW9uIGJlY2F1c2UgdHJ5L2NhdGNoZXMgZGVvcHRpbWl6ZSBpbiBjZXJ0YWluIGVuZ2luZXMuXG5cbnZhciBjYWNoZWRTZXRUaW1lb3V0O1xudmFyIGNhY2hlZENsZWFyVGltZW91dDtcblxuZnVuY3Rpb24gZGVmYXVsdFNldFRpbW91dCgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3NldFRpbWVvdXQgaGFzIG5vdCBiZWVuIGRlZmluZWQnKTtcbn1cbmZ1bmN0aW9uIGRlZmF1bHRDbGVhclRpbWVvdXQgKCkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2xlYXJUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG4oZnVuY3Rpb24gKCkge1xuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2Ygc2V0VGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gZGVmYXVsdFNldFRpbW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIGlmICh0eXBlb2YgY2xlYXJUaW1lb3V0ID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBkZWZhdWx0Q2xlYXJUaW1lb3V0O1xuICAgIH1cbn0gKCkpXG5mdW5jdGlvbiBydW5UaW1lb3V0KGZ1bikge1xuICAgIGlmIChjYWNoZWRTZXRUaW1lb3V0ID09PSBzZXRUaW1lb3V0KSB7XG4gICAgICAgIC8vbm9ybWFsIGVudmlyb21lbnRzIGluIHNhbmUgc2l0dWF0aW9uc1xuICAgICAgICByZXR1cm4gc2V0VGltZW91dChmdW4sIDApO1xuICAgIH1cbiAgICAvLyBpZiBzZXRUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkU2V0VGltZW91dCA9PT0gZGVmYXVsdFNldFRpbW91dCB8fCAhY2FjaGVkU2V0VGltZW91dCkgJiYgc2V0VGltZW91dCkge1xuICAgICAgICBjYWNoZWRTZXRUaW1lb3V0ID0gc2V0VGltZW91dDtcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dChmdW4sIDApO1xuICAgIH0gY2F0Y2goZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRTZXRUaW1lb3V0LmNhbGwobnVsbCwgZnVuLCAwKTtcbiAgICAgICAgfSBjYXRjaChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yXG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKHRoaXMsIGZ1biwgMCk7XG4gICAgICAgIH1cbiAgICB9XG5cblxufVxuZnVuY3Rpb24gcnVuQ2xlYXJUaW1lb3V0KG1hcmtlcikge1xuICAgIGlmIChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGNsZWFyVGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICAvLyBpZiBjbGVhclRpbWVvdXQgd2Fzbid0IGF2YWlsYWJsZSBidXQgd2FzIGxhdHRlciBkZWZpbmVkXG4gICAgaWYgKChjYWNoZWRDbGVhclRpbWVvdXQgPT09IGRlZmF1bHRDbGVhclRpbWVvdXQgfHwgIWNhY2hlZENsZWFyVGltZW91dCkgJiYgY2xlYXJUaW1lb3V0KSB7XG4gICAgICAgIGNhY2hlZENsZWFyVGltZW91dCA9IGNsZWFyVGltZW91dDtcbiAgICAgICAgcmV0dXJuIGNsZWFyVGltZW91dChtYXJrZXIpO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICAvLyB3aGVuIHdoZW4gc29tZWJvZHkgaGFzIHNjcmV3ZWQgd2l0aCBzZXRUaW1lb3V0IGJ1dCBubyBJLkUuIG1hZGRuZXNzXG4gICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIFdoZW4gd2UgYXJlIGluIEkuRS4gYnV0IHRoZSBzY3JpcHQgaGFzIGJlZW4gZXZhbGVkIHNvIEkuRS4gZG9lc24ndCAgdHJ1c3QgdGhlIGdsb2JhbCBvYmplY3Qgd2hlbiBjYWxsZWQgbm9ybWFsbHlcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbChudWxsLCBtYXJrZXIpO1xuICAgICAgICB9IGNhdGNoIChlKXtcbiAgICAgICAgICAgIC8vIHNhbWUgYXMgYWJvdmUgYnV0IHdoZW4gaXQncyBhIHZlcnNpb24gb2YgSS5FLiB0aGF0IG11c3QgaGF2ZSB0aGUgZ2xvYmFsIG9iamVjdCBmb3IgJ3RoaXMnLCBob3BmdWxseSBvdXIgY29udGV4dCBjb3JyZWN0IG90aGVyd2lzZSBpdCB3aWxsIHRocm93IGEgZ2xvYmFsIGVycm9yLlxuICAgICAgICAgICAgLy8gU29tZSB2ZXJzaW9ucyBvZiBJLkUuIGhhdmUgZGlmZmVyZW50IHJ1bGVzIGZvciBjbGVhclRpbWVvdXQgdnMgc2V0VGltZW91dFxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZENsZWFyVGltZW91dC5jYWxsKHRoaXMsIG1hcmtlcik7XG4gICAgICAgIH1cbiAgICB9XG5cblxuXG59XG52YXIgcXVldWUgPSBbXTtcbnZhciBkcmFpbmluZyA9IGZhbHNlO1xudmFyIGN1cnJlbnRRdWV1ZTtcbnZhciBxdWV1ZUluZGV4ID0gLTE7XG5cbmZ1bmN0aW9uIGNsZWFuVXBOZXh0VGljaygpIHtcbiAgICBpZiAoIWRyYWluaW5nIHx8ICFjdXJyZW50UXVldWUpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBkcmFpbmluZyA9IGZhbHNlO1xuICAgIGlmIChjdXJyZW50UXVldWUubGVuZ3RoKSB7XG4gICAgICAgIHF1ZXVlID0gY3VycmVudFF1ZXVlLmNvbmNhdChxdWV1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgIH1cbiAgICBpZiAocXVldWUubGVuZ3RoKSB7XG4gICAgICAgIGRyYWluUXVldWUoKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIGRyYWluUXVldWUoKSB7XG4gICAgaWYgKGRyYWluaW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdmFyIHRpbWVvdXQgPSBydW5UaW1lb3V0KGNsZWFuVXBOZXh0VGljayk7XG4gICAgZHJhaW5pbmcgPSB0cnVlO1xuXG4gICAgdmFyIGxlbiA9IHF1ZXVlLmxlbmd0aDtcbiAgICB3aGlsZShsZW4pIHtcbiAgICAgICAgY3VycmVudFF1ZXVlID0gcXVldWU7XG4gICAgICAgIHF1ZXVlID0gW107XG4gICAgICAgIHdoaWxlICgrK3F1ZXVlSW5kZXggPCBsZW4pIHtcbiAgICAgICAgICAgIGlmIChjdXJyZW50UXVldWUpIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50UXVldWVbcXVldWVJbmRleF0ucnVuKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcXVldWVJbmRleCA9IC0xO1xuICAgICAgICBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgfVxuICAgIGN1cnJlbnRRdWV1ZSA9IG51bGw7XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBydW5DbGVhclRpbWVvdXQodGltZW91dCk7XG59XG5cbnByb2Nlc3MubmV4dFRpY2sgPSBmdW5jdGlvbiAoZnVuKSB7XG4gICAgdmFyIGFyZ3MgPSBuZXcgQXJyYXkoYXJndW1lbnRzLmxlbmd0aCAtIDEpO1xuICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgICBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcXVldWUucHVzaChuZXcgSXRlbShmdW4sIGFyZ3MpKTtcbiAgICBpZiAocXVldWUubGVuZ3RoID09PSAxICYmICFkcmFpbmluZykge1xuICAgICAgICBydW5UaW1lb3V0KGRyYWluUXVldWUpO1xuICAgIH1cbn07XG5cbi8vIHY4IGxpa2VzIHByZWRpY3RpYmxlIG9iamVjdHNcbmZ1bmN0aW9uIEl0ZW0oZnVuLCBhcnJheSkge1xuICAgIHRoaXMuZnVuID0gZnVuO1xuICAgIHRoaXMuYXJyYXkgPSBhcnJheTtcbn1cbkl0ZW0ucHJvdG90eXBlLnJ1biA9IGZ1bmN0aW9uICgpIHtcbiAgICB0aGlzLmZ1bi5hcHBseShudWxsLCB0aGlzLmFycmF5KTtcbn07XG5wcm9jZXNzLnRpdGxlID0gJ2Jyb3dzZXInO1xucHJvY2Vzcy5icm93c2VyID0gdHJ1ZTtcbnByb2Nlc3MuZW52ID0ge307XG5wcm9jZXNzLmFyZ3YgPSBbXTtcbnByb2Nlc3MudmVyc2lvbiA9ICcnOyAvLyBlbXB0eSBzdHJpbmcgdG8gYXZvaWQgcmVnZXhwIGlzc3Vlc1xucHJvY2Vzcy52ZXJzaW9ucyA9IHt9O1xuXG5mdW5jdGlvbiBub29wKCkge31cblxucHJvY2Vzcy5vbiA9IG5vb3A7XG5wcm9jZXNzLmFkZExpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3Mub25jZSA9IG5vb3A7XG5wcm9jZXNzLm9mZiA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUxpc3RlbmVyID0gbm9vcDtcbnByb2Nlc3MucmVtb3ZlQWxsTGlzdGVuZXJzID0gbm9vcDtcbnByb2Nlc3MuZW1pdCA9IG5vb3A7XG5wcm9jZXNzLnByZXBlbmRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnByZXBlbmRPbmNlTGlzdGVuZXIgPSBub29wO1xuXG5wcm9jZXNzLmxpc3RlbmVycyA9IGZ1bmN0aW9uIChuYW1lKSB7IHJldHVybiBbXSB9XG5cbnByb2Nlc3MuYmluZGluZyA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmJpbmRpbmcgaXMgbm90IHN1cHBvcnRlZCcpO1xufTtcblxucHJvY2Vzcy5jd2QgPSBmdW5jdGlvbiAoKSB7IHJldHVybiAnLycgfTtcbnByb2Nlc3MuY2hkaXIgPSBmdW5jdGlvbiAoZGlyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdwcm9jZXNzLmNoZGlyIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5wcm9jZXNzLnVtYXNrID0gZnVuY3Rpb24oKSB7IHJldHVybiAwOyB9O1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IHNjcm9sbF9zeW5jXzEgPSByZXF1aXJlKFwiLi9zY3JvbGwtc3luY1wiKTtcbmNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuICAgIG9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihsaW5lKSB7XG4gICAgICAgIGNvbnN0IHsgcHJldmlvdXMgfSA9IHNjcm9sbF9zeW5jXzEuZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgICAgICB0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG4gICAgfVxuICAgIF91cGRhdGUoYmVmb3JlKSB7XG4gICAgICAgIHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG4gICAgICAgIHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG4gICAgICAgIHRoaXMuX2N1cnJlbnQgPSBiZWZvcmU7XG4gICAgfVxuICAgIF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQpIHtcbiAgICAgICAgaWYgKCFlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcbiAgICB9XG4gICAgX21hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQpIHtcbiAgICAgICAgaWYgKCFlbGVtZW50KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcbiAgICB9XG59XG5leHBvcnRzLkFjdGl2ZUxpbmVNYXJrZXIgPSBBY3RpdmVMaW5lTWFya2VyO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmZ1bmN0aW9uIG9uY2VEb2N1bWVudExvYWRlZChmKSB7XG4gICAgaWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlID09PSAndW5pbml0aWFsaXplZCcpIHtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgZigpO1xuICAgIH1cbn1cbmV4cG9ydHMub25jZURvY3VtZW50TG9hZGVkID0gb25jZURvY3VtZW50TG9hZGVkO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmV4cG9ydHMuY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZSkgPT4ge1xuICAgIHJldHVybiBuZXcgY2xhc3Mge1xuICAgICAgICBwb3N0TWVzc2FnZSh0eXBlLCBib2R5KSB7XG4gICAgICAgICAgICB2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgICAgICAgc291cmNlOiBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkuc291cmNlLFxuICAgICAgICAgICAgICAgIGJvZHlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcbn07XG4iLCIvKipcbiAqIGxvZGFzaCAoQ3VzdG9tIEJ1aWxkKSA8aHR0cHM6Ly9sb2Rhc2guY29tLz5cbiAqIEJ1aWxkOiBgbG9kYXNoIG1vZHVsYXJpemUgZXhwb3J0cz1cIm5wbVwiIC1vIC4vYFxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnMgPGh0dHBzOi8vanF1ZXJ5Lm9yZy8+XG4gKiBSZWxlYXNlZCB1bmRlciBNSVQgbGljZW5zZSA8aHR0cHM6Ly9sb2Rhc2guY29tL2xpY2Vuc2U+XG4gKiBCYXNlZCBvbiBVbmRlcnNjb3JlLmpzIDEuOC4zIDxodHRwOi8vdW5kZXJzY29yZWpzLm9yZy9MSUNFTlNFPlxuICogQ29weXJpZ2h0IEplcmVteSBBc2hrZW5hcywgRG9jdW1lbnRDbG91ZCBhbmQgSW52ZXN0aWdhdGl2ZSBSZXBvcnRlcnMgJiBFZGl0b3JzXG4gKi9cblxuLyoqIFVzZWQgYXMgdGhlIGBUeXBlRXJyb3JgIG1lc3NhZ2UgZm9yIFwiRnVuY3Rpb25zXCIgbWV0aG9kcy4gKi9cbnZhciBGVU5DX0VSUk9SX1RFWFQgPSAnRXhwZWN0ZWQgYSBmdW5jdGlvbic7XG5cbi8qKiBVc2VkIGFzIHJlZmVyZW5jZXMgZm9yIHZhcmlvdXMgYE51bWJlcmAgY29uc3RhbnRzLiAqL1xudmFyIE5BTiA9IDAgLyAwO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgc3ltYm9sVGFnID0gJ1tvYmplY3QgU3ltYm9sXSc7XG5cbi8qKiBVc2VkIHRvIG1hdGNoIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlc3BhY2UuICovXG52YXIgcmVUcmltID0gL15cXHMrfFxccyskL2c7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiYWQgc2lnbmVkIGhleGFkZWNpbWFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JhZEhleCA9IC9eWy0rXTB4WzAtOWEtZl0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmluYXJ5IHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc0JpbmFyeSA9IC9eMGJbMDFdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IG9jdGFsIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVJc09jdGFsID0gL14wb1swLTddKyQvaTtcblxuLyoqIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIHdpdGhvdXQgYSBkZXBlbmRlbmN5IG9uIGByb290YC4gKi9cbnZhciBmcmVlUGFyc2VJbnQgPSBwYXJzZUludDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBnbG9iYWxgIGZyb20gTm9kZS5qcy4gKi9cbnZhciBmcmVlR2xvYmFsID0gdHlwZW9mIGdsb2JhbCA9PSAnb2JqZWN0JyAmJiBnbG9iYWwgJiYgZ2xvYmFsLk9iamVjdCA9PT0gT2JqZWN0ICYmIGdsb2JhbDtcblxuLyoqIERldGVjdCBmcmVlIHZhcmlhYmxlIGBzZWxmYC4gKi9cbnZhciBmcmVlU2VsZiA9IHR5cGVvZiBzZWxmID09ICdvYmplY3QnICYmIHNlbGYgJiYgc2VsZi5PYmplY3QgPT09IE9iamVjdCAmJiBzZWxmO1xuXG4vKiogVXNlZCBhcyBhIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9iamVjdC4gKi9cbnZhciByb290ID0gZnJlZUdsb2JhbCB8fCBmcmVlU2VsZiB8fCBGdW5jdGlvbigncmV0dXJuIHRoaXMnKSgpO1xuXG4vKiogVXNlZCBmb3IgYnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGVcbiAqIFtgdG9TdHJpbmdUYWdgXShodHRwOi8vZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqZWN0VG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1heCA9IE1hdGgubWF4LFxuICAgIG5hdGl2ZU1pbiA9IE1hdGgubWluO1xuXG4vKipcbiAqIEdldHMgdGhlIHRpbWVzdGFtcCBvZiB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0aGF0IGhhdmUgZWxhcHNlZCBzaW5jZVxuICogdGhlIFVuaXggZXBvY2ggKDEgSmFudWFyeSAxOTcwIDAwOjAwOjAwIFVUQykuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAyLjQuMFxuICogQGNhdGVnb3J5IERhdGVcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIHRpbWVzdGFtcC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5kZWZlcihmdW5jdGlvbihzdGFtcCkge1xuICogICBjb25zb2xlLmxvZyhfLm5vdygpIC0gc3RhbXApO1xuICogfSwgXy5ub3coKSk7XG4gKiAvLyA9PiBMb2dzIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGl0IHRvb2sgZm9yIHRoZSBkZWZlcnJlZCBpbnZvY2F0aW9uLlxuICovXG52YXIgbm93ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiByb290LkRhdGUubm93KCk7XG59O1xuXG4vKipcbiAqIENyZWF0ZXMgYSBkZWJvdW5jZWQgZnVuY3Rpb24gdGhhdCBkZWxheXMgaW52b2tpbmcgYGZ1bmNgIHVudGlsIGFmdGVyIGB3YWl0YFxuICogbWlsbGlzZWNvbmRzIGhhdmUgZWxhcHNlZCBzaW5jZSB0aGUgbGFzdCB0aW1lIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gd2FzXG4gKiBpbnZva2VkLiBUaGUgZGVib3VuY2VkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYCBtZXRob2QgdG8gY2FuY2VsXG4gKiBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0byBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS5cbiAqIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZVxuICogbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZFxuICogd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbi4gU3Vic2VxdWVudFxuICogY2FsbHMgdG8gdGhlIGRlYm91bmNlZCBmdW5jdGlvbiByZXR1cm4gdGhlIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2BcbiAqIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8uZGVib3VuY2VgIGFuZCBgXy50aHJvdHRsZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBkZWJvdW5jZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byBkZWxheS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPWZhbHNlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0aW9ucy5tYXhXYWl0XVxuICogIFRoZSBtYXhpbXVtIHRpbWUgYGZ1bmNgIGlzIGFsbG93ZWQgdG8gYmUgZGVsYXllZCBiZWZvcmUgaXQncyBpbnZva2VkLlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBkZWJvdW5jZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGNvc3RseSBjYWxjdWxhdGlvbnMgd2hpbGUgdGhlIHdpbmRvdyBzaXplIGlzIGluIGZsdXguXG4gKiBqUXVlcnkod2luZG93KS5vbigncmVzaXplJywgXy5kZWJvdW5jZShjYWxjdWxhdGVMYXlvdXQsIDE1MCkpO1xuICpcbiAqIC8vIEludm9rZSBgc2VuZE1haWxgIHdoZW4gY2xpY2tlZCwgZGVib3VuY2luZyBzdWJzZXF1ZW50IGNhbGxzLlxuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIF8uZGVib3VuY2Uoc2VuZE1haWwsIDMwMCwge1xuICogICAnbGVhZGluZyc6IHRydWUsXG4gKiAgICd0cmFpbGluZyc6IGZhbHNlXG4gKiB9KSk7XG4gKlxuICogLy8gRW5zdXJlIGBiYXRjaExvZ2AgaXMgaW52b2tlZCBvbmNlIGFmdGVyIDEgc2Vjb25kIG9mIGRlYm91bmNlZCBjYWxscy5cbiAqIHZhciBkZWJvdW5jZWQgPSBfLmRlYm91bmNlKGJhdGNoTG9nLCAyNTAsIHsgJ21heFdhaXQnOiAxMDAwIH0pO1xuICogdmFyIHNvdXJjZSA9IG5ldyBFdmVudFNvdXJjZSgnL3N0cmVhbScpO1xuICogalF1ZXJ5KHNvdXJjZSkub24oJ21lc3NhZ2UnLCBkZWJvdW5jZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgZGVib3VuY2VkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCBkZWJvdW5jZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gZGVib3VuY2UoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGFzdEFyZ3MsXG4gICAgICBsYXN0VGhpcyxcbiAgICAgIG1heFdhaXQsXG4gICAgICByZXN1bHQsXG4gICAgICB0aW1lcklkLFxuICAgICAgbGFzdENhbGxUaW1lLFxuICAgICAgbGFzdEludm9rZVRpbWUgPSAwLFxuICAgICAgbGVhZGluZyA9IGZhbHNlLFxuICAgICAgbWF4aW5nID0gZmFsc2UsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgd2FpdCA9IHRvTnVtYmVyKHdhaXQpIHx8IDA7XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAhIW9wdGlvbnMubGVhZGluZztcbiAgICBtYXhpbmcgPSAnbWF4V2FpdCcgaW4gb3B0aW9ucztcbiAgICBtYXhXYWl0ID0gbWF4aW5nID8gbmF0aXZlTWF4KHRvTnVtYmVyKG9wdGlvbnMubWF4V2FpdCkgfHwgMCwgd2FpdCkgOiBtYXhXYWl0O1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cblxuICBmdW5jdGlvbiBpbnZva2VGdW5jKHRpbWUpIHtcbiAgICB2YXIgYXJncyA9IGxhc3RBcmdzLFxuICAgICAgICB0aGlzQXJnID0gbGFzdFRoaXM7XG5cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICByZXN1bHQgPSBmdW5jLmFwcGx5KHRoaXNBcmcsIGFyZ3MpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBsZWFkaW5nRWRnZSh0aW1lKSB7XG4gICAgLy8gUmVzZXQgYW55IGBtYXhXYWl0YCB0aW1lci5cbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgLy8gU3RhcnQgdGhlIHRpbWVyIGZvciB0aGUgdHJhaWxpbmcgZWRnZS5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIC8vIEludm9rZSB0aGUgbGVhZGluZyBlZGdlLlxuICAgIHJldHVybiBsZWFkaW5nID8gaW52b2tlRnVuYyh0aW1lKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbWFpbmluZ1dhaXQodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWUsXG4gICAgICAgIHJlc3VsdCA9IHdhaXQgLSB0aW1lU2luY2VMYXN0Q2FsbDtcblxuICAgIHJldHVybiBtYXhpbmcgPyBuYXRpdmVNaW4ocmVzdWx0LCBtYXhXYWl0IC0gdGltZVNpbmNlTGFzdEludm9rZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBzaG91bGRJbnZva2UodGltZSkge1xuICAgIHZhciB0aW1lU2luY2VMYXN0Q2FsbCA9IHRpbWUgLSBsYXN0Q2FsbFRpbWUsXG4gICAgICAgIHRpbWVTaW5jZUxhc3RJbnZva2UgPSB0aW1lIC0gbGFzdEludm9rZVRpbWU7XG5cbiAgICAvLyBFaXRoZXIgdGhpcyBpcyB0aGUgZmlyc3QgY2FsbCwgYWN0aXZpdHkgaGFzIHN0b3BwZWQgYW5kIHdlJ3JlIGF0IHRoZVxuICAgIC8vIHRyYWlsaW5nIGVkZ2UsIHRoZSBzeXN0ZW0gdGltZSBoYXMgZ29uZSBiYWNrd2FyZHMgYW5kIHdlJ3JlIHRyZWF0aW5nXG4gICAgLy8gaXQgYXMgdGhlIHRyYWlsaW5nIGVkZ2UsIG9yIHdlJ3ZlIGhpdCB0aGUgYG1heFdhaXRgIGxpbWl0LlxuICAgIHJldHVybiAobGFzdENhbGxUaW1lID09PSB1bmRlZmluZWQgfHwgKHRpbWVTaW5jZUxhc3RDYWxsID49IHdhaXQpIHx8XG4gICAgICAodGltZVNpbmNlTGFzdENhbGwgPCAwKSB8fCAobWF4aW5nICYmIHRpbWVTaW5jZUxhc3RJbnZva2UgPj0gbWF4V2FpdCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdGltZXJFeHBpcmVkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCk7XG4gICAgaWYgKHNob3VsZEludm9rZSh0aW1lKSkge1xuICAgICAgcmV0dXJuIHRyYWlsaW5nRWRnZSh0aW1lKTtcbiAgICB9XG4gICAgLy8gUmVzdGFydCB0aGUgdGltZXIuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCByZW1haW5pbmdXYWl0KHRpbWUpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRyYWlsaW5nRWRnZSh0aW1lKSB7XG4gICAgdGltZXJJZCA9IHVuZGVmaW5lZDtcblxuICAgIC8vIE9ubHkgaW52b2tlIGlmIHdlIGhhdmUgYGxhc3RBcmdzYCB3aGljaCBtZWFucyBgZnVuY2AgaGFzIGJlZW5cbiAgICAvLyBkZWJvdW5jZWQgYXQgbGVhc3Qgb25jZS5cbiAgICBpZiAodHJhaWxpbmcgJiYgbGFzdEFyZ3MpIHtcbiAgICAgIHJldHVybiBpbnZva2VGdW5jKHRpbWUpO1xuICAgIH1cbiAgICBsYXN0QXJncyA9IGxhc3RUaGlzID0gdW5kZWZpbmVkO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiBjYW5jZWwoKSB7XG4gICAgaWYgKHRpbWVySWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVySWQpO1xuICAgIH1cbiAgICBsYXN0SW52b2tlVGltZSA9IDA7XG4gICAgbGFzdEFyZ3MgPSBsYXN0Q2FsbFRpbWUgPSBsYXN0VGhpcyA9IHRpbWVySWQgPSB1bmRlZmluZWQ7XG4gIH1cblxuICBmdW5jdGlvbiBmbHVzaCgpIHtcbiAgICByZXR1cm4gdGltZXJJZCA9PT0gdW5kZWZpbmVkID8gcmVzdWx0IDogdHJhaWxpbmdFZGdlKG5vdygpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIGRlYm91bmNlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpLFxuICAgICAgICBpc0ludm9raW5nID0gc2hvdWxkSW52b2tlKHRpbWUpO1xuXG4gICAgbGFzdEFyZ3MgPSBhcmd1bWVudHM7XG4gICAgbGFzdFRoaXMgPSB0aGlzO1xuICAgIGxhc3RDYWxsVGltZSA9IHRpbWU7XG5cbiAgICBpZiAoaXNJbnZva2luZykge1xuICAgICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gbGVhZGluZ0VkZ2UobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICAgIGlmIChtYXhpbmcpIHtcbiAgICAgICAgLy8gSGFuZGxlIGludm9jYXRpb25zIGluIGEgdGlnaHQgbG9vcC5cbiAgICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAgICAgcmV0dXJuIGludm9rZUZ1bmMobGFzdENhbGxUaW1lKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRpbWVySWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBkZWJvdW5jZWQuY2FuY2VsID0gY2FuY2VsO1xuICBkZWJvdW5jZWQuZmx1c2ggPSBmbHVzaDtcbiAgcmV0dXJuIGRlYm91bmNlZDtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgdGhyb3R0bGVkIGZ1bmN0aW9uIHRoYXQgb25seSBpbnZva2VzIGBmdW5jYCBhdCBtb3N0IG9uY2UgcGVyXG4gKiBldmVyeSBgd2FpdGAgbWlsbGlzZWNvbmRzLiBUaGUgdGhyb3R0bGVkIGZ1bmN0aW9uIGNvbWVzIHdpdGggYSBgY2FuY2VsYFxuICogbWV0aG9kIHRvIGNhbmNlbCBkZWxheWVkIGBmdW5jYCBpbnZvY2F0aW9ucyBhbmQgYSBgZmx1c2hgIG1ldGhvZCB0b1xuICogaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uIFByb3ZpZGUgYG9wdGlvbnNgIHRvIGluZGljYXRlIHdoZXRoZXIgYGZ1bmNgXG4gKiBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGUgbGVhZGluZyBhbmQvb3IgdHJhaWxpbmcgZWRnZSBvZiB0aGUgYHdhaXRgXG4gKiB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWQgd2l0aCB0aGUgbGFzdCBhcmd1bWVudHMgcHJvdmlkZWQgdG8gdGhlXG4gKiB0aHJvdHRsZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnQgY2FsbHMgdG8gdGhlIHRocm90dGxlZCBmdW5jdGlvbiByZXR1cm4gdGhlXG4gKiByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgIGludm9jYXRpb24uXG4gKlxuICogKipOb3RlOioqIElmIGBsZWFkaW5nYCBhbmQgYHRyYWlsaW5nYCBvcHRpb25zIGFyZSBgdHJ1ZWAsIGBmdW5jYCBpc1xuICogaW52b2tlZCBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dCBvbmx5IGlmIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb25cbiAqIGlzIGludm9rZWQgbW9yZSB0aGFuIG9uY2UgZHVyaW5nIHRoZSBgd2FpdGAgdGltZW91dC5cbiAqXG4gKiBJZiBgd2FpdGAgaXMgYDBgIGFuZCBgbGVhZGluZ2AgaXMgYGZhbHNlYCwgYGZ1bmNgIGludm9jYXRpb24gaXMgZGVmZXJyZWRcbiAqIHVudGlsIHRvIHRoZSBuZXh0IHRpY2ssIHNpbWlsYXIgdG8gYHNldFRpbWVvdXRgIHdpdGggYSB0aW1lb3V0IG9mIGAwYC5cbiAqXG4gKiBTZWUgW0RhdmlkIENvcmJhY2hvJ3MgYXJ0aWNsZV0oaHR0cHM6Ly9jc3MtdHJpY2tzLmNvbS9kZWJvdW5jaW5nLXRocm90dGxpbmctZXhwbGFpbmVkLWV4YW1wbGVzLylcbiAqIGZvciBkZXRhaWxzIG92ZXIgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gYF8udGhyb3R0bGVgIGFuZCBgXy5kZWJvdW5jZWAuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byB0aHJvdHRsZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbd2FpdD0wXSBUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB0aHJvdHRsZSBpbnZvY2F0aW9ucyB0by5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucz17fV0gVGhlIG9wdGlvbnMgb2JqZWN0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5sZWFkaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy50cmFpbGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyB0aHJvdHRsZWQgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIC8vIEF2b2lkIGV4Y2Vzc2l2ZWx5IHVwZGF0aW5nIHRoZSBwb3NpdGlvbiB3aGlsZSBzY3JvbGxpbmcuXG4gKiBqUXVlcnkod2luZG93KS5vbignc2Nyb2xsJywgXy50aHJvdHRsZSh1cGRhdGVQb3NpdGlvbiwgMTAwKSk7XG4gKlxuICogLy8gSW52b2tlIGByZW5ld1Rva2VuYCB3aGVuIHRoZSBjbGljayBldmVudCBpcyBmaXJlZCwgYnV0IG5vdCBtb3JlIHRoYW4gb25jZSBldmVyeSA1IG1pbnV0ZXMuXG4gKiB2YXIgdGhyb3R0bGVkID0gXy50aHJvdHRsZShyZW5ld1Rva2VuLCAzMDAwMDAsIHsgJ3RyYWlsaW5nJzogZmFsc2UgfSk7XG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgdGhyb3R0bGVkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIHRocm90dGxlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgdGhyb3R0bGVkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIHRocm90dGxlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxlYWRpbmcgPSB0cnVlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIGlmIChpc09iamVjdChvcHRpb25zKSkge1xuICAgIGxlYWRpbmcgPSAnbGVhZGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy5sZWFkaW5nIDogbGVhZGluZztcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG4gIHJldHVybiBkZWJvdW5jZShmdW5jLCB3YWl0LCB7XG4gICAgJ2xlYWRpbmcnOiBsZWFkaW5nLFxuICAgICdtYXhXYWl0Jzogd2FpdCxcbiAgICAndHJhaWxpbmcnOiB0cmFpbGluZ1xuICB9KTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyB0aGVcbiAqIFtsYW5ndWFnZSB0eXBlXShodHRwOi8vd3d3LmVjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtZWNtYXNjcmlwdC1sYW5ndWFnZS10eXBlcylcbiAqIG9mIGBPYmplY3RgLiAoZS5nLiBhcnJheXMsIGZ1bmN0aW9ucywgb2JqZWN0cywgcmVnZXhlcywgYG5ldyBOdW1iZXIoMClgLCBhbmQgYG5ldyBTdHJpbmcoJycpYClcbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhbiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdCh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoXy5ub29wKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3QodmFsdWUpIHtcbiAgdmFyIHR5cGUgPSB0eXBlb2YgdmFsdWU7XG4gIHJldHVybiAhIXZhbHVlICYmICh0eXBlID09ICdvYmplY3QnIHx8IHR5cGUgPT0gJ2Z1bmN0aW9uJyk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UuIEEgdmFsdWUgaXMgb2JqZWN0LWxpa2UgaWYgaXQncyBub3QgYG51bGxgXG4gKiBhbmQgaGFzIGEgYHR5cGVvZmAgcmVzdWx0IG9mIFwib2JqZWN0XCIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgb2JqZWN0LWxpa2UsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdExpa2Uoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoXy5ub29wKTtcbiAqIC8vID0+IGZhbHNlXG4gKlxuICogXy5pc09iamVjdExpa2UobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdExpa2UodmFsdWUpIHtcbiAgcmV0dXJuICEhdmFsdWUgJiYgdHlwZW9mIHZhbHVlID09ICdvYmplY3QnO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGNsYXNzaWZpZWQgYXMgYSBgU3ltYm9sYCBwcmltaXRpdmUgb3Igb2JqZWN0LlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgc3ltYm9sLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNTeW1ib2woU3ltYm9sLml0ZXJhdG9yKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzU3ltYm9sKCdhYmMnKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzU3ltYm9sKHZhbHVlKSB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT0gJ3N5bWJvbCcgfHxcbiAgICAoaXNPYmplY3RMaWtlKHZhbHVlKSAmJiBvYmplY3RUb1N0cmluZy5jYWxsKHZhbHVlKSA9PSBzeW1ib2xUYWcpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIGB2YWx1ZWAgdG8gYSBudW1iZXIuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIHByb2Nlc3MuXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSBudW1iZXIuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8udG9OdW1iZXIoMy4yKTtcbiAqIC8vID0+IDMuMlxuICpcbiAqIF8udG9OdW1iZXIoTnVtYmVyLk1JTl9WQUxVRSk7XG4gKiAvLyA9PiA1ZS0zMjRcbiAqXG4gKiBfLnRvTnVtYmVyKEluZmluaXR5KTtcbiAqIC8vID0+IEluZmluaXR5XG4gKlxuICogXy50b051bWJlcignMy4yJyk7XG4gKiAvLyA9PiAzLjJcbiAqL1xuZnVuY3Rpb24gdG9OdW1iZXIodmFsdWUpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PSAnbnVtYmVyJykge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICBpZiAoaXNTeW1ib2wodmFsdWUpKSB7XG4gICAgcmV0dXJuIE5BTjtcbiAgfVxuICBpZiAoaXNPYmplY3QodmFsdWUpKSB7XG4gICAgdmFyIG90aGVyID0gdHlwZW9mIHZhbHVlLnZhbHVlT2YgPT0gJ2Z1bmN0aW9uJyA/IHZhbHVlLnZhbHVlT2YoKSA6IHZhbHVlO1xuICAgIHZhbHVlID0gaXNPYmplY3Qob3RoZXIpID8gKG90aGVyICsgJycpIDogb3RoZXI7XG4gIH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykge1xuICAgIHJldHVybiB2YWx1ZSA9PT0gMCA/IHZhbHVlIDogK3ZhbHVlO1xuICB9XG4gIHZhbHVlID0gdmFsdWUucmVwbGFjZShyZVRyaW0sICcnKTtcbiAgdmFyIGlzQmluYXJ5ID0gcmVJc0JpbmFyeS50ZXN0KHZhbHVlKTtcbiAgcmV0dXJuIChpc0JpbmFyeSB8fCByZUlzT2N0YWwudGVzdCh2YWx1ZSkpXG4gICAgPyBmcmVlUGFyc2VJbnQodmFsdWUuc2xpY2UoMiksIGlzQmluYXJ5ID8gMiA6IDgpXG4gICAgOiAocmVJc0JhZEhleC50ZXN0KHZhbHVlKSA/IE5BTiA6ICt2YWx1ZSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gdGhyb3R0bGU7XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index bddc3b86ac..c75da6b854 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -1,2 +1,2 @@ -!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},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=8)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let s=void 0;function o(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=o,t.getSettings=function(){if(s)return s;if(s=o("data-settings"))return s;throw new Error("Could not load settings")}},,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(9),o=n(11);window.cspAlerter=new s.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(0),o=n(10);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=s.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const n=document.createElement("a");n.innerText=e.cspAlertMessageText,n.setAttribute("id","code-csp-warning"),n.setAttribute("title",e.cspAlertMessageTitle),n.setAttribute("role","button"),n.setAttribute("aria-label",e.cspAlertMessageLabel),n.onclick=()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})},document.body.appendChild(n)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}}]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2NzcC50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zdHJpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsInQiLCJtb2RlIiwiX19lc01vZHVsZSIsIm5zIiwiY3JlYXRlIiwia2V5IiwiYmluZCIsIm4iLCJvYmplY3QiLCJwcm9wZXJ0eSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwicCIsInMiLCJjYWNoZWRTZXR0aW5ncyIsInVuZGVmaW5lZCIsImdldERhdGEiLCJlbGVtZW50IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImRhdGEiLCJnZXRBdHRyaWJ1dGUiLCJKU09OIiwicGFyc2UiLCJFcnJvciIsImdldFNldHRpbmdzIiwiY3NwXzEiLCJsb2FkaW5nXzEiLCJ3aW5kb3ciLCJjc3BBbGVydGVyIiwiQ3NwQWxlcnRlciIsInN0eWxlTG9hZGluZ01vbml0b3IiLCJTdHlsZUxvYWRpbmdNb25pdG9yIiwic2V0dGluZ3NfMSIsInN0cmluZ3NfMSIsInRoaXMiLCJkaWRTaG93IiwiZGlkSGF2ZUNzcFdhcm5pbmciLCJhZGRFdmVudExpc3RlbmVyIiwib25Dc3BXYXJuaW5nIiwiZXZlbnQiLCJwb3N0ZXIiLCJtZXNzYWdpbmciLCJzaG93Q3NwV2FybmluZyIsInN0cmluZ3MiLCJnZXRTdHJpbmdzIiwic2V0dGluZ3MiLCJkaXNhYmxlU2VjdXJpdHlXYXJuaW5ncyIsIm5vdGlmaWNhdGlvbiIsImNyZWF0ZUVsZW1lbnQiLCJpbm5lclRleHQiLCJjc3BBbGVydE1lc3NhZ2VUZXh0Iiwic2V0QXR0cmlidXRlIiwiY3NwQWxlcnRNZXNzYWdlVGl0bGUiLCJjc3BBbGVydE1lc3NhZ2VMYWJlbCIsIm9uY2xpY2siLCJwb3N0TWVzc2FnZSIsInNvdXJjZSIsImJvZHkiLCJhcHBlbmRDaGlsZCIsInN0b3JlIiwidW5sb2FkZWRTdHlsZXMiLCJmaW5pc2hlZExvYWRpbmciLCJvblN0eWxlTG9hZEVycm9yIiwidGFyZ2V0IiwiZGF0YXNldCIsInB1c2giLCJsaW5rIiwiZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSIsIm9uZXJyb3IiLCJsZW5ndGgiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZLEVBQU1DLElBQUtMLEtBS2hFWixFQUFvQmtCLEVBQUksU0FBU2hCLEdBQ1gsb0JBQVhpQixRQUEwQkEsT0FBT0MsYUFDMUNOLE9BQU9DLGVBQWViLEVB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LEtBQUswQiIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gOCk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xubGV0IGNhY2hlZFNldHRpbmdzID0gdW5kZWZpbmVkO1xuZnVuY3Rpb24gZ2V0RGF0YShrZXkpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICBjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5leHBvcnRzLmdldERhdGEgPSBnZXREYXRhO1xuZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKSB7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgY2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG4gICAgaWYgKGNhY2hlZFNldHRpbmdzKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWRTZXR0aW5ncztcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuZXhwb3J0cy5nZXRTZXR0aW5ncyA9IGdldFNldHRpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IGNzcF8xID0gcmVxdWlyZShcIi4vY3NwXCIpO1xuY29uc3QgbG9hZGluZ18xID0gcmVxdWlyZShcIi4vbG9hZGluZ1wiKTtcbndpbmRvdy5jc3BBbGVydGVyID0gbmV3IGNzcF8xLkNzcEFsZXJ0ZXIoKTtcbndpbmRvdy5zdHlsZUxvYWRpbmdNb25pdG9yID0gbmV3IGxvYWRpbmdfMS5TdHlsZUxvYWRpbmdNb25pdG9yKCk7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3Qgc2V0dGluZ3NfMSA9IHJlcXVpcmUoXCIuL3NldHRpbmdzXCIpO1xuY29uc3Qgc3RyaW5nc18xID0gcmVxdWlyZShcIi4vc3RyaW5nc1wiKTtcbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuY2xhc3MgQ3NwQWxlcnRlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gZmFsc2U7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3NlY3VyaXR5cG9saWN5dmlvbGF0aW9uJywgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBpZiAoZXZlbnQgJiYgZXZlbnQuZGF0YSAmJiBldmVudC5kYXRhLm5hbWUgPT09ICd2c2NvZGUtZGlkLWJsb2NrLXN2ZycpIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uQ3NwV2FybmluZygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLm1lc3NhZ2luZyA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0NzcFdhcm5pbmcoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBvbkNzcFdhcm5pbmcoKSB7XG4gICAgICAgIHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgfVxuICAgIHNob3dDc3BXYXJuaW5nKCkge1xuICAgICAgICBjb25zdCBzdHJpbmdzID0gc3RyaW5nc18xLmdldFN0cmluZ3MoKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBzZXR0aW5nc18xLmdldFNldHRpbmdzKCk7XG4gICAgICAgIGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kaWRTaG93ID0gdHJ1ZTtcbiAgICAgICAgY29uc3Qgbm90aWZpY2F0aW9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYScpO1xuICAgICAgICBub3RpZmljYXRpb24uaW5uZXJUZXh0ID0gc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUZXh0O1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3RpdGxlJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUaXRsZSk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZUxhYmVsKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnc2hvd1ByZXZpZXdTZWN1cml0eVNlbGVjdG9yJywgeyBzb3VyY2U6IHNldHRpbmdzLnNvdXJjZSB9KTtcbiAgICAgICAgfTtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuICAgIH1cbn1cbmV4cG9ydHMuQ3NwQWxlcnRlciA9IENzcEFsZXJ0ZXI7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuZnVuY3Rpb24gZ2V0U3RyaW5ncygpIHtcbiAgICBjb25zdCBzdG9yZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKHN0b3JlKSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBzdG9yZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RyaW5ncycpO1xuICAgICAgICBpZiAoZGF0YSkge1xuICAgICAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzdHJpbmdzJyk7XG59XG5leHBvcnRzLmdldFN0cmluZ3MgPSBnZXRTdHJpbmdzO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jbGFzcyBTdHlsZUxvYWRpbmdNb25pdG9yIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy51bmxvYWRlZFN0eWxlcyA9IFtdO1xuICAgICAgICB0aGlzLmZpbmlzaGVkTG9hZGluZyA9IGZhbHNlO1xuICAgICAgICBjb25zdCBvblN0eWxlTG9hZEVycm9yID0gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBzb3VyY2UgPSBldmVudC50YXJnZXQuZGF0YXNldC5zb3VyY2U7XG4gICAgICAgICAgICB0aGlzLnVubG9hZGVkU3R5bGVzLnB1c2goc291cmNlKTtcbiAgICAgICAgfTtcbiAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGxpbmsgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS11c2VyLXN0eWxlJykpIHtcbiAgICAgICAgICAgICAgICBpZiAobGluay5kYXRhc2V0LnNvdXJjZSkge1xuICAgICAgICAgICAgICAgICAgICBsaW5rLm9uZXJyb3IgPSBvblN0eWxlTG9hZEVycm9yO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgKCkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnVubG9hZGVkU3R5bGVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuZmluaXNoZWRMb2FkaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIGlmICh0aGlzLnBvc3Rlcikge1xuICAgICAgICAgICAgICAgIHRoaXMucG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0UG9zdGVyKHBvc3Rlcikge1xuICAgICAgICB0aGlzLnBvc3RlciA9IHBvc3RlcjtcbiAgICAgICAgaWYgKHRoaXMuZmluaXNoZWRMb2FkaW5nKSB7XG4gICAgICAgICAgICBwb3N0ZXIucG9zdE1lc3NhZ2UoJ3ByZXZpZXdTdHlsZUxvYWRFcnJvcicsIHsgdW5sb2FkZWRTdHlsZXM6IHRoaXMudW5sb2FkZWRTdHlsZXMgfSk7XG4gICAgICAgIH1cbiAgICB9XG59XG5leHBvcnRzLlN0eWxlTG9hZGluZ01vbml0b3IgPSBTdHlsZUxvYWRpbmdNb25pdG9yO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +!function(e){var t={};function n(s){if(t[s])return t[s].exports;var o=t[s]={i:s,l:!1,exports:{}};return e[s].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(s,o,function(t){return e[t]}.bind(null,o));return s},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 s=void 0;function o(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=o,t.getSettings=function(){if(s)return s;if(s=o("data-settings"))return s;throw new Error("Could not load settings")}},,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(12),o=n(14);window.cspAlerter=new s.CspAlerter,window.styleLoadingMonitor=new o.StyleLoadingMonitor},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=n(0),o=n(13);t.CspAlerter=class{constructor(){this.didShow=!1,this.didHaveCspWarning=!1,document.addEventListener("securitypolicyviolation",()=>{this.onCspWarning()}),window.addEventListener("message",e=>{e&&e.data&&"vscode-did-block-svg"===e.data.name&&this.onCspWarning()})}setPoster(e){this.messaging=e,this.didHaveCspWarning&&this.showCspWarning()}onCspWarning(){this.didHaveCspWarning=!0,this.showCspWarning()}showCspWarning(){const e=o.getStrings(),t=s.getSettings();if(this.didShow||t.disableSecurityWarnings||!this.messaging)return;this.didShow=!0;const n=document.createElement("a");n.innerText=e.cspAlertMessageText,n.setAttribute("id","code-csp-warning"),n.setAttribute("title",e.cspAlertMessageTitle),n.setAttribute("role","button"),n.setAttribute("aria-label",e.cspAlertMessageLabel),n.onclick=()=>{this.messaging.postMessage("showPreviewSecuritySelector",{source:t.source})},document.body.appendChild(n)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getStrings=function(){const e=document.getElementById("vscode-markdown-preview-data");if(e){const t=e.getAttribute("data-strings");if(t)return JSON.parse(t)}throw new Error("Could not load strings")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.StyleLoadingMonitor=class{constructor(){this.unloadedStyles=[],this.finishedLoading=!1;const e=e=>{const t=e.target.dataset.source;this.unloadedStyles.push(t)};window.addEventListener("DOMContentLoaded",()=>{for(const t of document.getElementsByClassName("code-user-style"))t.dataset.source&&(t.onerror=e)}),window.addEventListener("load",()=>{this.unloadedStyles.length&&(this.finishedLoading=!0,this.poster&&this.poster.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles}))})}setPoster(e){this.poster=e,this.finishedLoading&&e.postMessage("previewStyleLoadError",{unloadedStyles:this.unloadedStyles})}}}]); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2NzcC50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zdHJpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJlX18iLCJtb2R1bGVJZCIsImV4cG9ydHMiLCJtb2R1bGUiLCJpIiwibCIsIm1vZHVsZXMiLCJjYWxsIiwibSIsImMiLCJkIiwibmFtZSIsImdldHRlciIsIm8iLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImVudW1lcmFibGUiLCJnZXQiLCJyIiwiU3ltYm9sIiwidG9TdHJpbmdUYWciLCJ2YWx1ZSIsInQiLCJtb2RlIiwiX19lc01vZHVsZSIsIm5zIiwiY3JlYXRlIiwia2V5IiwiYmluZCIsIm4iLCJvYmplY3QiLCJwcm9wZXJ0eSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwicCIsInMiLCJjYWNoZWRTZXR0aW5ncyIsInVuZGVmaW5lZCIsImdldERhdGEiLCJlbGVtZW50IiwiZG9jdW1lbnQiLCJnZXRFbGVtZW50QnlJZCIsImRhdGEiLCJnZXRBdHRyaWJ1dGUiLCJKU09OIiwicGFyc2UiLCJFcnJvciIsImdldFNldHRpbmdzIiwiY3NwXzEiLCJsb2FkaW5nXzEiLCJ3aW5kb3ciLCJjc3BBbGVydGVyIiwiQ3NwQWxlcnRlciIsInN0eWxlTG9hZGluZ01vbml0b3IiLCJTdHlsZUxvYWRpbmdNb25pdG9yIiwic2V0dGluZ3NfMSIsInN0cmluZ3NfMSIsInRoaXMiLCJkaWRTaG93IiwiZGlkSGF2ZUNzcFdhcm5pbmciLCJhZGRFdmVudExpc3RlbmVyIiwib25Dc3BXYXJuaW5nIiwiZXZlbnQiLCJwb3N0ZXIiLCJtZXNzYWdpbmciLCJzaG93Q3NwV2FybmluZyIsInN0cmluZ3MiLCJnZXRTdHJpbmdzIiwic2V0dGluZ3MiLCJkaXNhYmxlU2VjdXJpdHlXYXJuaW5ncyIsIm5vdGlmaWNhdGlvbiIsImNyZWF0ZUVsZW1lbnQiLCJpbm5lclRleHQiLCJjc3BBbGVydE1lc3NhZ2VUZXh0Iiwic2V0QXR0cmlidXRlIiwiY3NwQWxlcnRNZXNzYWdlVGl0bGUiLCJjc3BBbGVydE1lc3NhZ2VMYWJlbCIsIm9uY2xpY2siLCJwb3N0TWVzc2FnZSIsInNvdXJjZSIsImJvZHkiLCJhcHBlbmRDaGlsZCIsInN0b3JlIiwidW5sb2FkZWRTdHlsZXMiLCJmaW5pc2hlZExvYWRpbmciLCJvblN0eWxlTG9hZEVycm9yIiwidGFyZ2V0IiwiZGF0YXNldCIsInB1c2giLCJsaW5rIiwiZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSIsIm9uZXJyb3IiLCJsZW5ndGgiXSwibWFwcGluZ3MiOiJhQUNFLElBQUlBLEVBQW1CLEdBR3ZCLFNBQVNDLEVBQW9CQyxHQUc1QixHQUFHRixFQUFpQkUsR0FDbkIsT0FBT0YsRUFBaUJFLEdBQVVDLFFBR25DLElBQUlDLEVBQVNKLEVBQWlCRSxHQUFZLENBQ3pDRyxFQUFHSCxFQUNISSxHQUFHLEVBQ0hILFFBQVMsSUFVVixPQU5BSSxFQUFRTCxHQUFVTSxLQUFLSixFQUFPRCxRQUFTQyxFQUFRQSxFQUFPRCxRQUFTRixHQUcvREcsRUFBT0UsR0FBSSxFQUdKRixFQUFPRCxRQUtmRixFQUFvQlEsRUFBSUYsRUFHeEJOLEVBQW9CUyxFQUFJVixFQUd4QkMsRUFBb0JVLEVBQUksU0FBU1IsRUFBU1MsRUFBTUMsR0FDM0NaLEVBQW9CYSxFQUFFWCxFQUFTUyxJQUNsQ0csT0FBT0MsZUFBZWIsRUFBU1MsRUFBTSxDQUFFSyxZQUFZLEVBQU1DLElBQUtMLEtBS2hFWixFQUFvQmtCLEVBQUksU0FBU2hCLEdBQ1gsb0JBQVhpQixRQUEwQkEsT0FBT0MsYUFDMUNOLE9BQU9DLGVBQWViLEVB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LEtBQUswQiIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMTEpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBjc3BfMSA9IHJlcXVpcmUoXCIuL2NzcFwiKTtcbmNvbnN0IGxvYWRpbmdfMSA9IHJlcXVpcmUoXCIuL2xvYWRpbmdcIik7XG53aW5kb3cuY3NwQWxlcnRlciA9IG5ldyBjc3BfMS5Dc3BBbGVydGVyKCk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvciA9IG5ldyBsb2FkaW5nXzEuU3R5bGVMb2FkaW5nTW9uaXRvcigpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmNvbnN0IHNldHRpbmdzXzEgPSByZXF1aXJlKFwiLi9zZXR0aW5nc1wiKTtcbmNvbnN0IHN0cmluZ3NfMSA9IHJlcXVpcmUoXCIuL3N0cmluZ3NcIik7XG4vKipcbiAqIFNob3dzIGFuIGFsZXJ0IHdoZW4gdGhlcmUgaXMgYSBjb250ZW50IHNlY3VyaXR5IHBvbGljeSB2aW9sYXRpb24uXG4gKi9cbmNsYXNzIENzcEFsZXJ0ZXIge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmRpZFNob3cgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5kaWRIYXZlQ3NwV2FybmluZyA9IGZhbHNlO1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzZWN1cml0eXBvbGljeXZpb2xhdGlvbicsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMub25Dc3BXYXJuaW5nKCk7XG4gICAgICAgIH0pO1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKGV2ZW50ICYmIGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5uYW1lID09PSAndnNjb2RlLWRpZC1ibG9jay1zdmcnKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5vbkNzcFdhcm5pbmcoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldFBvc3Rlcihwb3N0ZXIpIHtcbiAgICAgICAgdGhpcy5tZXNzYWdpbmcgPSBwb3N0ZXI7XG4gICAgICAgIGlmICh0aGlzLmRpZEhhdmVDc3BXYXJuaW5nKSB7XG4gICAgICAgICAgICB0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgb25Dc3BXYXJuaW5nKCkge1xuICAgICAgICB0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5zaG93Q3NwV2FybmluZygpO1xuICAgIH1cbiAgICBzaG93Q3NwV2FybmluZygpIHtcbiAgICAgICAgY29uc3Qgc3RyaW5ncyA9IHN0cmluZ3NfMS5nZXRTdHJpbmdzKCk7XG4gICAgICAgIGNvbnN0IHNldHRpbmdzID0gc2V0dGluZ3NfMS5nZXRTZXR0aW5ncygpO1xuICAgICAgICBpZiAodGhpcy5kaWRTaG93IHx8IHNldHRpbmdzLmRpc2FibGVTZWN1cml0eVdhcm5pbmdzIHx8ICF0aGlzLm1lc3NhZ2luZykge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZGlkU2hvdyA9IHRydWU7XG4gICAgICAgIGNvbnN0IG5vdGlmaWNhdGlvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICAgICAgbm90aWZpY2F0aW9uLmlubmVyVGV4dCA9IHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGV4dDtcbiAgICAgICAgbm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgnaWQnLCAnY29kZS1jc3Atd2FybmluZycpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCd0aXRsZScsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGl0bGUpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xuICAgICAgICBub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VMYWJlbCk7XG4gICAgICAgIG5vdGlmaWNhdGlvbi5vbmNsaWNrID0gKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5tZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3Nob3dQcmV2aWV3U2VjdXJpdHlTZWxlY3RvcicsIHsgc291cmNlOiBzZXR0aW5ncy5zb3VyY2UgfSk7XG4gICAgICAgIH07XG4gICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQobm90aWZpY2F0aW9uKTtcbiAgICB9XG59XG5leHBvcnRzLkNzcEFsZXJ0ZXIgPSBDc3BBbGVydGVyO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmZ1bmN0aW9uIGdldFN0cmluZ3MoKSB7XG4gICAgY29uc3Qgc3RvcmUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuICAgIGlmIChzdG9yZSkge1xuICAgICAgICBjb25zdCBkYXRhID0gc3RvcmUuZ2V0QXR0cmlidXRlKCdkYXRhLXN0cmluZ3MnKTtcbiAgICAgICAgaWYgKGRhdGEpIHtcbiAgICAgICAgICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc3RyaW5ncycpO1xufVxuZXhwb3J0cy5nZXRTdHJpbmdzID0gZ2V0U3RyaW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY2xhc3MgU3R5bGVMb2FkaW5nTW9uaXRvciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMudW5sb2FkZWRTdHlsZXMgPSBbXTtcbiAgICAgICAgdGhpcy5maW5pc2hlZExvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgY29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc291cmNlID0gZXZlbnQudGFyZ2V0LmRhdGFzZXQuc291cmNlO1xuICAgICAgICAgICAgdGhpcy51bmxvYWRlZFN0eWxlcy5wdXNoKHNvdXJjZSk7XG4gICAgICAgIH07XG4gICAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4ge1xuICAgICAgICAgICAgZm9yIChjb25zdCBsaW5rIG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtdXNlci1zdHlsZScpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgbGluay5vbmVycm9yID0gb25TdHlsZUxvYWRFcnJvcjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmZpbmlzaGVkTG9hZGluZyA9IHRydWU7XG4gICAgICAgICAgICBpZiAodGhpcy5wb3N0ZXIpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldFBvc3Rlcihwb3N0ZXIpIHtcbiAgICAgICAgdGhpcy5wb3N0ZXIgPSBwb3N0ZXI7XG4gICAgICAgIGlmICh0aGlzLmZpbmlzaGVkTG9hZGluZykge1xuICAgICAgICAgICAgcG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuICAgICAgICB9XG4gICAgfVxufVxuZXhwb3J0cy5TdHlsZUxvYWRpbmdNb25pdG9yID0gU3R5bGVMb2FkaW5nTW9uaXRvcjtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ 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 6b13b21893..c09e811854 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -18,8 +18,8 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); -// Set VS Code state -let state = getData<{ line: number; fragment: string; }>('data-state'); +const state = { ...vscode.getState(), ...getData('data-state') }; +// Make sure to sync VS Code state here vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -32,23 +32,35 @@ window.onload = () => { }; onceDocumentLoaded(() => { + const scrollProgress = state.scrollProgress; + + if (typeof scrollProgress === 'number' && !settings.fragment) { + setImmediate(() => { + scrollDisabled = true; + window.scrollTo(0, scrollProgress * document.body.clientHeight); + }); + return; + } + if (settings.scrollPreviewWithEditor) { - setTimeout(() => { + setImmediate(() => { // Try to scroll to fragment if available - if (state.fragment) { - const element = getLineElementForFragment(state.fragment); + if (settings.fragment) { + state.fragment = undefined; + vscode.setState(state); + + const element = getLineElementForFragment(settings.fragment); if (element) { scrollDisabled = true; scrollToRevealSourceLine(element.line); } } else { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { + if (!isNaN(settings.line!)) { scrollDisabled = true; - scrollToRevealSourceLine(initialLine); + scrollToRevealSourceLine(settings.line!); } } - }, 0); + }); } }); @@ -58,9 +70,10 @@ const onUpdateView = (() => { scrollToRevealSourceLine(line); }, 50); - return (line: number, settings: any) => { + return (line: number) => { if (!isNaN(line)) { - settings.line = line; + state.line = line; + doScroll(line); } }; @@ -91,6 +104,7 @@ let updateImageSizes = throttle(() => { window.addEventListener('resize', () => { scrollDisabled = true; + updateScrollProgress(); updateImageSizes(); }, true); @@ -105,7 +119,7 @@ window.addEventListener('message', event => { break; case 'updateView': - onUpdateView(event.data.line, settings); + onUpdateView(event.data.line); break; } }, false); @@ -165,15 +179,20 @@ document.addEventListener('click', event => { }, true); window.addEventListener('scroll', throttle(() => { + updateScrollProgress(); + if (scrollDisabled) { scrollDisabled = false; } else { const line = getEditorLineNumberForPageOffset(window.scrollY); if (typeof line === 'number' && !isNaN(line)) { messaging.postMessage('revealLine', { line }); - state.line = line; - vscode.setState(state); } } }, 50)); +function updateScrollProgress() { + state.scrollProgress = window.scrollY / document.body.clientHeight; + vscode.setState(state); +} + diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index fcb35a0791..61799eff65 100644 --- a/extensions/markdown-language-features/preview-src/settings.ts +++ b/extensions/markdown-language-features/preview-src/settings.ts @@ -5,7 +5,8 @@ export interface PreviewSettings { readonly source: string; - readonly line: number; + readonly line?: number; + readonly fragment?: string readonly lineCount: number; readonly scrollPreviewWithEditor?: boolean; readonly scrollEditorWithPreview: boolean; diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index 53223f8e24..7bc765259b 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -66,6 +66,11 @@ export class OpenDocumentLinkCommand implements Command { } } + const stat = await vscode.workspace.fs.stat(resource); + if (stat.type === vscode.FileType.Directory) { + return vscode.commands.executeCommand('revealInExplorer', resource); + } + return vscode.workspace.openTextDocument(resource) .then(document => vscode.window.showTextDocument(document, column)) .then(editor => this.tryRevealLine(editor, args.fragment)); diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index ce626ce7de..a711e7cc50 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -13,7 +13,7 @@ import { Disposable } from '../util/dispose'; import * as nls from 'vscode-nls'; import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider } from '../markdownExtensions'; import { isMarkdownFile } from '../util/file'; import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; import { WebviewResourceProvider, normalizeResource } from '../util/resources'; @@ -61,10 +61,14 @@ interface PreviewStyleLoadErrorMessage extends WebviewMessage { } export class PreviewDocumentVersion { - public constructor( - public readonly resource: vscode.Uri, - public readonly version: number, - ) { } + + private readonly resource: vscode.Uri; + private readonly version: number; + + public constructor(document: vscode.TextDocument) { + this.resource = document.uri; + this.version = document.version; + } public equals(other: PreviewDocumentVersion): boolean { return this.resource.fsPath === other.resource.fsPath @@ -72,102 +76,86 @@ export class PreviewDocumentVersion { } } -interface DynamicPreviewInput { - readonly resource: vscode.Uri; - readonly resourceColumn: vscode.ViewColumn; - readonly locked: boolean; - readonly line?: number; +interface MarkdownPreviewDelegate { + getTitle?(resource: vscode.Uri): string; + getAdditionalState(): {}, + openPreviewLinkToMarkdownFile(markdownLink: vscode.Uri, fragment: string): void; } -export class DynamicMarkdownPreview extends Disposable { +class StartingScrollLine { + public readonly type = 'line'; - public static readonly viewType = 'markdown.preview'; + constructor( + public readonly line: number, + ) { } +} + +class StartingScrollFragment { + public readonly type = 'fragment'; + + constructor( + public readonly fragment: string, + ) { } +} + +type StartingScrollLocation = StartingScrollLine | StartingScrollFragment; + +class MarkdownPreview extends Disposable implements WebviewResourceProvider { private readonly delay = 300; - private _resource: vscode.Uri; - private readonly _resourceColumn: vscode.ViewColumn; + private readonly _resource: vscode.Uri; + private readonly _webviewPanel: vscode.WebviewPanel; - private _locked: boolean; - - private readonly editor: vscode.WebviewPanel; private throttleTimer: any; - private line: number | undefined = undefined; + + private line: number | undefined; + private scrollToFragment: string | undefined; + private firstUpdate = true; private currentVersion?: PreviewDocumentVersion; private isScrolling = false; private _disposed: boolean = false; - private imageInfo: { id: string, width: number, height: number; }[] = []; - private scrollToFragment: string | undefined; + private imageInfo: { readonly id: string, readonly width: number, readonly height: number; }[] = []; - public static revive( - input: DynamicPreviewInput, + constructor( webview: vscode.WebviewPanel, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider, - ): DynamicMarkdownPreview { - webview.webview.options = DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions); - webview.title = DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked); - - return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); - } - - public static create( - input: DynamicPreviewInput, - previewColumn: vscode.ViewColumn, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider - ): DynamicMarkdownPreview { - const webview = vscode.window.createWebviewPanel( - DynamicMarkdownPreview.viewType, - DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked), - previewColumn, { - enableFindWidget: true, - ...DynamicMarkdownPreview.getWebviewOptions(input.resource, contributionProvider.contributions) - }); - - return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); - } - - private constructor( - webview: vscode.WebviewPanel, - input: DynamicPreviewInput, + resource: vscode.Uri, + startingScroll: StartingScrollLocation | undefined, + private readonly delegate: MarkdownPreviewDelegate, private readonly _contentProvider: MarkdownContentProvider, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, - topmostLineMonitor: TopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, ) { super(); - this._resource = input.resource; - this._resourceColumn = input.resourceColumn; - this._locked = input.locked; - this.editor = webview; - if (!isNaN(input.line!)) { - this.line = input.line; + + this._webviewPanel = webview; + this._resource = resource; + + switch (startingScroll?.type) { + case 'line': + if (!isNaN(startingScroll.line!)) { + this.line = startingScroll.line; + } + break; + + case 'fragment': + this.scrollToFragment = startingScroll.fragment; + break; } - this._register(this.editor.onDidDispose(() => { - this.dispose(); - })); - - this._register(this.editor.onDidChangeViewState(e => { - this._onDidChangeViewStateEmitter.fire(e); - })); - this._register(_contributionProvider.onContributionsChanged(() => { setImmediate(() => this.refresh()); })); - this._register(this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { + this._register(vscode.workspace.onDidChangeTextDocument(event => { + if (this.isPreviewOf(event.document.uri)) { + this.refresh(); + } + })); + + this._register(this._webviewPanel.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; } @@ -194,158 +182,50 @@ export class DynamicMarkdownPreview extends Disposable { break; case 'previewStyleLoadError': - vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); + vscode.window.showWarningMessage( + localize('onPreviewStyleLoadError', + "Could not load 'markdown.styles': {0}", + e.body.unloadedStyles.join(', '))); break; } })); - this._register(vscode.workspace.onDidChangeTextDocument(event => { - if (this.isPreviewOf(event.document.uri)) { - this.refresh(); - } - })); - - this._register(topmostLineMonitor.onDidChanged(event => { - if (this.isPreviewOf(event.resource)) { - this.updateForView(event.resource, event.line); - } - })); - - this._register(vscode.window.onDidChangeTextEditorSelection(event => { - if (this.isPreviewOf(event.textEditor.document.uri)) { - this.postMessage({ - type: 'onDidChangeTextEditorSelection', - line: event.selections[0].active.line, - source: this.resource.toString() - }); - } - })); - - this._register(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor && isMarkdownFile(editor.document) && !this._locked) { - this.update(editor.document.uri, false); - } - })); - - this.doUpdate(); + this.updatePreview(); } - private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); - public readonly onDispose = this._onDisposeEmitter.event; - - private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter()); - public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; + dispose() { + super.dispose(); + this._disposed = true; + clearTimeout(this.throttleTimer); + } public get resource(): vscode.Uri { return this._resource; } - public get resourceColumn(): vscode.ViewColumn { - return this._resourceColumn; - } - public get state() { return { - resource: this.resource.toString(), - locked: this._locked, + resource: this._resource.toString(), line: this.line, - resourceColumn: this.resourceColumn, imageInfo: this.imageInfo, - fragment: this.scrollToFragment + fragment: this.scrollToFragment, + ...this.delegate.getAdditionalState(), }; } - public dispose() { - if (this._disposed) { - return; - } - - this._disposed = true; - this._onDisposeEmitter.fire(); - this._onDisposeEmitter.dispose(); - - this.editor.dispose(); - super.dispose(); - } - - public update(resource: vscode.Uri, isRefresh = true) { - // Reposition scroll preview, position scroll to the top if active text editor - // doesn't corresponds with preview - const editor = vscode.window.activeTextEditor; - if (editor) { - if (!isRefresh || this._previewConfigurations.loadAndCacheConfiguration(this._resource).scrollEditorWithPreview) { - if (editor.document.uri.fsPath === resource.fsPath) { - this.line = getVisibleLine(editor); - } else { - this.line = 0; - } - } - } - - // If we have changed resources, cancel any pending updates - const isResourceChange = resource.fsPath !== this._resource.fsPath; - if (isResourceChange) { - clearTimeout(this.throttleTimer); - this.throttleTimer = undefined; - } - - this._resource = resource; - + public refresh() { // Schedule update if none is pending if (!this.throttleTimer) { - if (isResourceChange || this.firstUpdate) { - this.doUpdate(isRefresh); + if (this.firstUpdate) { + this.updatePreview(true); } else { - this.throttleTimer = setTimeout(() => this.doUpdate(isRefresh), this.delay); + this.throttleTimer = setTimeout(() => this.updatePreview(true), this.delay); } } this.firstUpdate = false; } - public refresh() { - this.update(this._resource, true); - } - - public updateConfiguration() { - if (this._previewConfigurations.hasConfigurationChanged(this._resource)) { - this.refresh(); - } - } - - public get position(): vscode.ViewColumn | undefined { - return this.editor.viewColumn; - } - - public matchesResource( - otherResource: vscode.Uri, - otherPosition: vscode.ViewColumn | undefined, - otherLocked: boolean - ): boolean { - if (this.position !== otherPosition) { - return false; - } - - if (this._locked) { - return otherLocked && this.isPreviewOf(otherResource); - } else { - return !otherLocked; - } - } - - public matches(otherPreview: DynamicMarkdownPreview): boolean { - return this.matchesResource(otherPreview._resource, otherPreview.position, otherPreview._locked); - } - - public reveal(viewColumn: vscode.ViewColumn) { - this.editor.reveal(viewColumn); - } - - public toggleLock() { - this._locked = !this._locked; - this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked); - } - private get iconPath() { const root = path.join(this._contributionProvider.extensionPath, 'media'); return { @@ -354,18 +234,18 @@ export class DynamicMarkdownPreview extends Disposable { }; } - private isPreviewOf(resource: vscode.Uri): boolean { + public isPreviewOf(resource: vscode.Uri): boolean { return this._resource.fsPath === resource.fsPath; } - private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { - return locked - ? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath)) - : localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)); + public postMessage(msg: any) { + if (!this._disposed) { + this._webviewPanel.webview.postMessage(msg); + } } - private updateForView(resource: vscode.Uri, topLine: number | undefined) { - if (!this.isPreviewOf(resource)) { + public scrollTo(topLine: number) { + if (this._disposed) { return; } @@ -374,36 +254,26 @@ export class DynamicMarkdownPreview extends Disposable { return; } - if (typeof topLine === 'number') { - this._logger.log('updateForView', { markdownFile: resource }); - this.line = topLine; - this.postMessage({ - type: 'updateView', - line: topLine, - source: resource.toString() - }); - } + this._logger.log('updateForView', { markdownFile: this._resource }); + this.line = topLine; + this.postMessage({ + type: 'updateView', + line: topLine, + source: this._resource.toString() + }); } - private postMessage(msg: any) { - if (!this._disposed) { - this.editor.webview.postMessage(msg); - } - } + private async updatePreview(forceUpdate?: boolean): Promise { + clearTimeout(this.throttleTimer); + this.throttleTimer = undefined; - private async doUpdate(forceUpdate?: boolean): Promise { if (this._disposed) { return; } - const markdownResource = this._resource; - - clearTimeout(this.throttleTimer); - this.throttleTimer = undefined; - let document: vscode.TextDocument; try { - document = await vscode.workspace.openTextDocument(markdownResource); + document = await vscode.workspace.openTextDocument(this._resource); } catch { await this.showFileNotFoundError(); return; @@ -413,61 +283,24 @@ export class DynamicMarkdownPreview extends Disposable { return; } - const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version); + const pendingVersion = new PreviewDocumentVersion(document); if (!forceUpdate && this.currentVersion?.equals(pendingVersion)) { if (this.line) { - this.updateForView(markdownResource, this.line); + this.scrollTo(this.line); } return; } this.currentVersion = pendingVersion; - if (this._resource === markdownResource) { - const self = this; - const resourceProvider: WebviewResourceProvider = { - asWebviewUri: (resource) => { - return this.editor.webview.asWebviewUri(normalizeResource(markdownResource, resource)); - }, - get cspSource() { return self.editor.webview.cspSource; } - }; - const content = await this._contentProvider.provideTextDocumentContent(document, resourceProvider, this._previewConfigurations, this.line, this.state); - // Another call to `doUpdate` may have happened. - // Make sure we are still updating for the correct document - if (this.currentVersion && this.currentVersion.equals(pendingVersion)) { - this.setContent(content); - } + const content = await this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state); + + // Another call to `doUpdate` may have happened. + // Make sure we are still updating for the correct document + if (this.currentVersion?.equals(pendingVersion)) { + this.setContent(content); } } - private static getWebviewOptions( - resource: vscode.Uri, - contributions: MarkdownContributions - ): vscode.WebviewOptions { - return { - enableScripts: true, - localResourceRoots: DynamicMarkdownPreview.getLocalResourceRoots(resource, contributions) - }; - } - - private static getLocalResourceRoots( - base: vscode.Uri, - contributions: MarkdownContributions - ): ReadonlyArray { - const baseRoots = Array.from(contributions.previewResourceRoots); - - const folder = vscode.workspace.getWorkspaceFolder(base); - if (folder) { - const workspaceRoots = vscode.workspace.workspaceFolders?.map(folder => folder.uri); - if (workspaceRoots) { - baseRoots.push(...workspaceRoots); - } - } else if (!base.scheme || base.scheme === 'file') { - baseRoots.push(vscode.Uri.file(path.dirname(base.fsPath))); - } - - return baseRoots.map(root => normalizeResource(base, root)); - } - private onDidScrollPreview(line: number) { this.line = line; @@ -513,23 +346,55 @@ export class DynamicMarkdownPreview extends Disposable { } private async showFileNotFoundError() { - this.setContent(this._contentProvider.provideFileNotFoundContent(this._resource)); + this._webviewPanel.webview.html = this._contentProvider.provideFileNotFoundContent(this._resource); } private setContent(html: string): void { - this.editor.title = DynamicMarkdownPreview.getPreviewTitle(this._resource, this._locked); - this.editor.iconPath = this.iconPath; - this.editor.webview.options = DynamicMarkdownPreview.getWebviewOptions(this._resource, this._contributionProvider.contributions); - this.editor.webview.html = html; + if (this._disposed) { + return; + } + + if (this.delegate.getTitle) { + this._webviewPanel.title = this.delegate.getTitle(this._resource); + } + this._webviewPanel.iconPath = this.iconPath; + this._webviewPanel.webview.options = this.getWebviewOptions(); + + this._webviewPanel.webview.html = html; } + private getWebviewOptions(): vscode.WebviewOptions { + return { + enableScripts: true, + localResourceRoots: this.getLocalResourceRoots() + }; + } + + private getLocalResourceRoots(): ReadonlyArray { + const baseRoots = Array.from(this._contributionProvider.contributions.previewResourceRoots); + + const folder = vscode.workspace.getWorkspaceFolder(this._resource); + if (folder) { + const workspaceRoots = vscode.workspace.workspaceFolders?.map(folder => folder.uri); + if (workspaceRoots) { + baseRoots.push(...workspaceRoots); + } + } else if (!this._resource.scheme || this._resource.scheme === 'file') { + baseRoots.push(vscode.Uri.file(path.dirname(this._resource.fsPath))); + } + + return baseRoots.map(root => normalizeResource(this._resource, root)); + } + + private async onDidClickPreviewLink(href: string) { let [hrefPath, fragment] = decodeURIComponent(href).split('#'); // We perviously already resolve absolute paths. // Now make sure we handle relative file paths if (hrefPath[0] !== '/') { - hrefPath = path.join(path.dirname(this.resource.path), hrefPath); + // Fix #93691, use this.resource.fsPath instead of this.resource.path + hrefPath = path.join(path.dirname(this.resource.fsPath), hrefPath); } const config = vscode.workspace.getConfiguration('markdown', this.resource); @@ -537,14 +402,332 @@ export class DynamicMarkdownPreview extends Disposable { if (openLinks === 'inPreview') { const markdownLink = await resolveLinkToMarkdownFile(hrefPath); if (markdownLink) { - if (fragment) { - this.scrollToFragment = fragment; - } - this.update(markdownLink); + this.delegate.openPreviewLinkToMarkdownFile(markdownLink, fragment); return; } } vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource }); } + + //#region WebviewResourceProvider + + asWebviewUri(resource: vscode.Uri) { + return this._webviewPanel.webview.asWebviewUri(normalizeResource(this._resource, resource)); + } + + get cspSource() { + return this._webviewPanel.webview.cspSource; + } + + //#endregion } + +export interface ManagedMarkdownPreview { + + readonly resource: vscode.Uri; + readonly resourceColumn: vscode.ViewColumn; + + readonly onDispose: vscode.Event; + readonly onDidChangeViewState: vscode.Event; + + dispose(): void; + + refresh(): void; + updateConfiguration(): void; + + matchesResource( + otherResource: vscode.Uri, + otherPosition: vscode.ViewColumn | undefined, + otherLocked: boolean + ): boolean; +} + +export class StaticMarkdownPreview extends Disposable implements ManagedMarkdownPreview { + + public static revive( + resource: vscode.Uri, + webview: vscode.WebviewPanel, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + contributionProvider: MarkdownContributionProvider, + ): StaticMarkdownPreview { + return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider); + } + + private readonly preview: MarkdownPreview; + + private constructor( + private readonly _webviewPanel: vscode.WebviewPanel, + resource: vscode.Uri, + contentProvider: MarkdownContentProvider, + private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + contributionProvider: MarkdownContributionProvider, + ) { + super(); + + this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, undefined, { + getAdditionalState: () => { return {}; }, + openPreviewLinkToMarkdownFile: () => { /* todo */ } + }, contentProvider, _previewConfigurations, logger, contributionProvider)); + + this._register(this._webviewPanel.onDidDispose(() => { + this.dispose(); + })); + + this._register(this._webviewPanel.onDidChangeViewState(e => { + this._onDidChangeViewState.fire(e); + })); + } + + private readonly _onDispose = this._register(new vscode.EventEmitter()); + public readonly onDispose = this._onDispose.event; + + private readonly _onDidChangeViewState = this._register(new vscode.EventEmitter()); + public readonly onDidChangeViewState = this._onDidChangeViewState.event; + + dispose() { + this._onDispose.fire(); + super.dispose(); + } + + public matchesResource( + _otherResource: vscode.Uri, + _otherPosition: vscode.ViewColumn | undefined, + _otherLocked: boolean + ): boolean { + return false; + } + + public refresh() { + this.preview.refresh(); + } + + public updateConfiguration() { + if (this._previewConfigurations.hasConfigurationChanged(this.preview.resource)) { + this.refresh(); + } + } + + public get resource() { + return this.preview.resource; + } + + public get resourceColumn() { + return this._webviewPanel.viewColumn || vscode.ViewColumn.One; + } +} + +interface DynamicPreviewInput { + readonly resource: vscode.Uri; + readonly resourceColumn: vscode.ViewColumn; + readonly locked: boolean; + readonly line?: number; +} + +/** + * A + */ +export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdownPreview { + + public static readonly viewType = 'markdown.preview'; + + private readonly _resourceColumn: vscode.ViewColumn; + private _locked: boolean; + + private readonly _webviewPanel: vscode.WebviewPanel; + private _preview: MarkdownPreview; + + public static revive( + input: DynamicPreviewInput, + webview: vscode.WebviewPanel, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + topmostLineMonitor: TopmostLineMonitor, + contributionProvider: MarkdownContributionProvider, + ): DynamicMarkdownPreview { + return new DynamicMarkdownPreview(webview, input, + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + } + + public static create( + input: DynamicPreviewInput, + previewColumn: vscode.ViewColumn, + contentProvider: MarkdownContentProvider, + previewConfigurations: MarkdownPreviewConfigurationManager, + logger: Logger, + topmostLineMonitor: TopmostLineMonitor, + contributionProvider: MarkdownContributionProvider + ): DynamicMarkdownPreview { + const webview = vscode.window.createWebviewPanel( + DynamicMarkdownPreview.viewType, + DynamicMarkdownPreview.getPreviewTitle(input.resource, input.locked), + previewColumn, { enableFindWidget: true, }); + + return new DynamicMarkdownPreview(webview, input, + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + } + + private constructor( + webview: vscode.WebviewPanel, + input: DynamicPreviewInput, + private readonly _contentProvider: MarkdownContentProvider, + private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, + private readonly _logger: Logger, + private readonly _topmostLineMonitor: TopmostLineMonitor, + private readonly _contributionProvider: MarkdownContributionProvider, + ) { + super(); + + this._webviewPanel = webview; + + this._resourceColumn = input.resourceColumn; + this._locked = input.locked; + + this._preview = this.createPreview(input.resource, typeof input.line === 'number' ? new StartingScrollLine(input.line) : undefined); + + this._register(webview.onDidDispose(() => { this.dispose(); })); + + this._register(this._webviewPanel.onDidChangeViewState(e => { + this._onDidChangeViewStateEmitter.fire(e); + })); + + this._register(this._topmostLineMonitor.onDidChanged(event => { + if (this._preview.isPreviewOf(event.resource)) { + this._preview.scrollTo(event.line); + } + })); + + this._register(vscode.window.onDidChangeTextEditorSelection(event => { + if (this._preview.isPreviewOf(event.textEditor.document.uri)) { + this._preview.postMessage({ + type: 'onDidChangeTextEditorSelection', + line: event.selections[0].active.line, + source: this._preview.resource.toString() + }); + } + })); + + this._register(vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor && isMarkdownFile(editor.document) && !this._locked && !this._preview.isPreviewOf(editor.document.uri)) { + const line = getVisibleLine(editor); + this.update(editor.document.uri, line ? new StartingScrollLine(line) : undefined); + } + })); + } + + private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); + public readonly onDispose = this._onDisposeEmitter.event; + + private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter()); + public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; + + dispose() { + this._preview.dispose(); + this._webviewPanel.dispose(); + + this._onDisposeEmitter.fire(); + this._onDisposeEmitter.dispose(); + super.dispose(); + } + + public get resource() { + return this._preview.resource; + } + + public get resourceColumn() { + return this._resourceColumn; + } + + public reveal(viewColumn: vscode.ViewColumn) { + this._webviewPanel.reveal(viewColumn); + } + + public refresh() { + this._preview.refresh(); + } + + public updateConfiguration() { + if (this._previewConfigurations.hasConfigurationChanged(this._preview.resource)) { + this.refresh(); + } + } + + public update(newResource: vscode.Uri, scrollLocation?: StartingScrollLocation) { + if (this._preview.isPreviewOf(newResource)) { + switch (scrollLocation?.type) { + case 'line': + this._preview.scrollTo(scrollLocation.line); + return; + + case 'fragment': + // Workaround. For fragments, just reload the entire preview + break; + + default: + return; + } + } + + this._preview.dispose(); + this._preview = this.createPreview(newResource, scrollLocation); + } + + public toggleLock() { + this._locked = !this._locked; + this._webviewPanel.title = DynamicMarkdownPreview.getPreviewTitle(this._preview.resource, this._locked); + } + + private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string { + return locked + ? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath)) + : localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)); + } + + public get position(): vscode.ViewColumn | undefined { + return this._webviewPanel.viewColumn; + } + + public matchesResource( + otherResource: vscode.Uri, + otherPosition: vscode.ViewColumn | undefined, + otherLocked: boolean + ): boolean { + if (this.position !== otherPosition) { + return false; + } + + if (this._locked) { + return otherLocked && this._preview.isPreviewOf(otherResource); + } else { + return !otherLocked; + } + } + + public matches(otherPreview: DynamicMarkdownPreview): boolean { + return this.matchesResource(otherPreview._preview.resource, otherPreview.position, otherPreview._locked); + } + + private createPreview(resource: vscode.Uri, startingScroll?: StartingScrollLocation): MarkdownPreview { + return new MarkdownPreview(this._webviewPanel, resource, startingScroll, { + getTitle: (resource) => DynamicMarkdownPreview.getPreviewTitle(resource, this._locked), + getAdditionalState: () => { + return { + resourceColumn: this.resourceColumn, + locked: this._locked, + }; + }, + openPreviewLinkToMarkdownFile: (link: vscode.Uri, fragment?: string) => { + this.update(link, fragment ? new StartingScrollFragment(fragment) : undefined); + } + }, + this._contentProvider, + this._previewConfigurations, + this._logger, + this._contributionProvider); + } +} + diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 6970c98ec0..5a73cb7a01 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -8,7 +8,7 @@ import { Logger } from '../logger'; import { MarkdownContributionProvider } from '../markdownExtensions'; import { disposeAll, Disposable } from '../util/dispose'; import { TopmostLineMonitor } from '../util/topmostLineMonitor'; -import { DynamicMarkdownPreview } from './preview'; +import { DynamicMarkdownPreview, StaticMarkdownPreview, ManagedMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContentProvider } from './previewContentProvider'; @@ -18,9 +18,9 @@ export interface DynamicPreviewSettings { readonly locked: boolean; } -class PreviewStore extends Disposable { +class PreviewStore extends Disposable { - private readonly _previews = new Set(); + private readonly _previews = new Set(); public dispose(): void { super.dispose(); @@ -30,11 +30,11 @@ class PreviewStore extends Disposable { this._previews.clear(); } - [Symbol.iterator](): Iterator { + [Symbol.iterator](): Iterator { return this._previews[Symbol.iterator](); } - public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): DynamicMarkdownPreview | undefined { + public get(resource: vscode.Uri, previewSettings: DynamicPreviewSettings): T | undefined { for (const preview of this._previews) { if (preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)) { return preview; @@ -43,11 +43,11 @@ class PreviewStore extends Disposable { return undefined; } - public add(preview: DynamicMarkdownPreview) { + public add(preview: T) { this._previews.add(preview); } - public delete(preview: DynamicMarkdownPreview) { + public delete(preview: T) { this._previews.delete(preview); } } @@ -58,10 +58,10 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview private readonly _topmostLineMonitor = new TopmostLineMonitor(); private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager(); - private readonly _dynamicPreviews = this._register(new PreviewStore()); - private readonly _staticPreviews = this._register(new PreviewStore()); + private readonly _dynamicPreviews = this._register(new PreviewStore()); + private readonly _staticPreviews = this._register(new PreviewStore()); - private _activePreview: DynamicMarkdownPreview | undefined = undefined; + private _activePreview: ManagedMarkdownPreview | undefined = undefined; private readonly customEditorViewType = 'vscode.markdown.preview.editor'; @@ -117,7 +117,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview public toggleLock() { const preview = this._activePreview; - if (preview) { + if (preview instanceof DynamicMarkdownPreview) { preview.toggleLock(); // Close any previews that are now redundant, such as having two dynamic previews in the same editor group @@ -133,6 +133,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview webview: vscode.WebviewPanel, state: any ): Promise { + console.log(state); const resource = vscode.Uri.parse(state.resource); const locked = state.locked; const line = state.line; @@ -150,21 +151,16 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this.registerDynamicPreview(preview); } - public async openCustomDocument(uri: vscode.Uri) { - return new vscode.CustomDocument(uri); - } - public async resolveCustomTextEditor( document: vscode.TextDocument, webview: vscode.WebviewPanel ): Promise { - const preview = DynamicMarkdownPreview.revive( - { resource: document.uri, locked: false, resourceColumn: vscode.ViewColumn.One }, + const preview = StaticMarkdownPreview.revive( + document.uri, webview, this._contentProvider, this._previewConfigurations, this._logger, - this._topmostLineMonitor, this._contributions); this.registerStaticPreview(preview); } @@ -207,7 +203,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview return preview; } - private registerStaticPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview { + private registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview { this._staticPreviews.add(preview); preview.onDispose(() => { @@ -218,7 +214,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview return preview; } - private trackActive(preview: DynamicMarkdownPreview): void { + private trackActive(preview: ManagedMarkdownPreview): void { preview.onDidChangeViewState(({ webviewPanel }) => { this.setPreviewActiveContext(webviewPanel.active); this._activePreview = webviewPanel.active ? preview : undefined; diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 2c2a204464..0e7899fafd 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode'; import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; -import { Schemes, isOfScheme } from './util/links'; +import { MarkdownFileExtensions, Schemes, isOfScheme } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; @@ -258,7 +258,9 @@ export class MarkdownEngine { } } - if (uri.fragment) { + const extname = path.extname(uri.fsPath); + + if (uri.fragment && (extname === '' || MarkdownFileExtensions.includes(extname))) { uri = uri.with({ fragment: this.slugifier.fromHeading(uri.fragment).value }); diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index fd0a7e173c..8cce8221e8 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -32,3 +32,15 @@ export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | export function isOfScheme(scheme: string, link: string): boolean { return link.toLowerCase().startsWith(scheme); } + +export const MarkdownFileExtensions: readonly string[] = [ + '.md', + '.mkd', + '.mdwn', + '.mdown', + '.markdown', + '.markdn', + '.mdtxt', + '.mdtext', + '.workbook', +]; diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts index d9e9e543a6..257b7ea1ba 100644 --- a/extensions/notebook/src/book/bookTreeView.ts +++ b/extensions/notebook/src/book/bookTreeView.ts @@ -138,7 +138,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { await book.initializeContents().then(() => { - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(undefined); }); } @@ -154,7 +154,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider 0 ? this.books[this.books.length - 1] : undefined; } - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(undefined); } } catch (e) { vscode.window.showErrorMessage(loc.closeBookError(book.root, e instanceof Error ? e.message : e)); @@ -313,7 +313,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider -1) { this.books.splice(untitledBookIndex, 1); this.currentBook = undefined; - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(undefined); vscode.commands.executeCommand('bookTreeView.openBook', destinationUri.fsPath, false, undefined); } } diff --git a/extensions/notebook/src/test/common/stubs.ts b/extensions/notebook/src/test/common/stubs.ts index dbc18e68eb..4283290dd0 100644 --- a/extensions/notebook/src/test/common/stubs.ts +++ b/extensions/notebook/src/test/common/stubs.ts @@ -12,6 +12,7 @@ export class MockExtensionContext implements vscode.ExtensionContext { workspaceState: vscode.Memento; globalState: vscode.Memento; extensionPath: string; + extensionUri: vscode.Uri; asAbsolutePath(relativePath: string): string { return relativePath; } diff --git a/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts index 45ad130a0a..da073e91ba 100644 --- a/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts +++ b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts @@ -70,7 +70,7 @@ describe('Manage Package Dialog', () => { displayName: 'dl2', name: 'nl2' }; - testContext.onClick.fire(); + testContext.onClick.fire(undefined); testContext.dialog.verify(x => x.changeLocation('nl2'), TypeMoq.Times.once()); testContext.dialog.verify(x => x.resetPages(), TypeMoq.Times.once()); @@ -100,7 +100,7 @@ describe('Manage Package Dialog', () => { displayName: 'dl2', name: 'nl2' }; - testContext.onClick.fire(); + testContext.onClick.fire(undefined); testContext.dialog.verify(x => x.changeLocation('nl2'), TypeMoq.Times.once()); testContext.dialog.verify(x => x.showErrorMessage(TypeMoq.It.isAny()), TypeMoq.Times.once()); diff --git a/extensions/powershell/language-configuration.json b/extensions/powershell/language-configuration.json index f30c0f5f05..719b5f81b5 100644 --- a/extensions/powershell/language-configuration.json +++ b/extensions/powershell/language-configuration.json @@ -12,6 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], + { "open": "@'", "close": "\n'@", "notIn": ["string", "comment"]}, + { "open": "@\"", "close": "\n\"@", "notIn": ["string", "comment"]}, { "open": "\"", "close": "\"", "notIn": ["string"]}, { "open": "'", "close": "'", "notIn": ["string", "comment"]}, ["<#", "#>"] @@ -29,4 +31,4 @@ "end": "^\\s*#[eE]nd[rR]egion\\b" } } -} \ No newline at end of file +} diff --git a/extensions/powershell/package.json b/extensions/powershell/package.json index 232acb1a7d..fb45b704b8 100644 --- a/extensions/powershell/package.json +++ b/extensions/powershell/package.json @@ -21,7 +21,7 @@ }], "snippets": [{ "language": "powershell", - "path": "./snippets/powershell.json" + "path": "./snippets/powershell.code-snippets" }] }, "scripts": { diff --git a/extensions/powershell/snippets/powershell.json b/extensions/powershell/snippets/powershell.code-snippets similarity index 100% rename from extensions/powershell/snippets/powershell.json rename to extensions/powershell/snippets/powershell.code-snippets diff --git a/extensions/sql-database-projects/src/controllers/databaseProjectTreeViewProvider.ts b/extensions/sql-database-projects/src/controllers/databaseProjectTreeViewProvider.ts index 354a2a9052..23b96784a3 100644 --- a/extensions/sql-database-projects/src/controllers/databaseProjectTreeViewProvider.ts +++ b/extensions/sql-database-projects/src/controllers/databaseProjectTreeViewProvider.ts @@ -56,6 +56,6 @@ export class SqlDatabaseProjectTreeViewProvider implements vscode.TreeDataProvid } this.roots = newRoots; - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(undefined); } } diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 89f0a5beec..54e8ece8b8 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -17,7 +17,9 @@ "menu.background": "#252526", "menu.foreground": "#CCCCCC", "statusBarItem.remoteForeground": "#FFF", - "statusBarItem.remoteBackground": "#16825D" + "statusBarItem.remoteBackground": "#16825D", + "sideBarSectionHeader.background": "#0000", + "sideBarSectionHeader.border": "#ccc3" }, "semanticHighlighting": true } diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 03e62612d6..560fd159a0 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -97,6 +97,16 @@ "foreground": "#9CDCFE" } }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#D4D4D4", + } + }, { "name": "Object keys, TS grammar specific", "scope": [ diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 9ea03a9e31..fa1e1dbc3c 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -17,7 +17,9 @@ "settings.textInputBorder": "#CECECE", "settings.numberInputBorder": "#CECECE", "statusBarItem.remoteForeground": "#FFF", - "statusBarItem.remoteBackground": "#16825D" + "statusBarItem.remoteBackground": "#16825D", + "sideBarSectionHeader.background": "#0000", + "sideBarSectionHeader.border": "#61616130" }, "semanticHighlighting": true } diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index faa2b836c2..8f65dc5b4f 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -97,6 +97,16 @@ "foreground": "#001080" } }, + { + "name": "Constants and enums", + "scope": [ + "variable.other.constant", + "variable.other.enummember" + ], + "settings": { + "foreground": "#000000", + } + }, { "name": "Object keys, TS grammar specific", "scope": [ diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json index ea3c2c2b89..38fc902a52 100644 --- a/extensions/vscode-account/package.json +++ b/extensions/vscode-account/package.json @@ -14,6 +14,7 @@ "activationEvents": [ "*" ], + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "main": "./out/extension.js", "contributes": { "commands": [ @@ -56,6 +57,7 @@ }, "dependencies": { "uuid": "^3.3.3", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.1" } } diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/vscode-account/src/AADHelper.ts index f232dfe99e..623c4926d8 100644 --- a/extensions/vscode-account/src/AADHelper.ts +++ b/extensions/vscode-account/src/AADHelper.ts @@ -25,7 +25,10 @@ interface IToken { expiresAt?: number; // UNIX epoch time at which token will expire refreshToken: string; - accountName: string; + account: { + displayName: string; + id: string; + }; scope: string; sessionId: string; // The account id + the scope } @@ -44,7 +47,10 @@ interface IStoredSession { id: string; refreshToken: string; scope: string; // Scopes are alphabetized and joined with a space - accountName: string; + account: { + displayName: string, + id: string + } } function parseQuery(uri: vscode.Uri) { @@ -76,6 +82,9 @@ export class AzureActiveDirectoryService { } public async initialize(): Promise { + // TODO remove, temporary migration + await keychain.migrateToken(); + const storedData = await keychain.getToken(); if (storedData) { try { @@ -90,7 +99,10 @@ export class AzureActiveDirectoryService { this._tokens.push({ accessToken: undefined, refreshToken: session.refreshToken, - accountName: session.accountName, + account: { + displayName: session.account.displayName, + id: session.account.id + }, scope: session.scope, sessionId: session.id }); @@ -122,7 +134,7 @@ export class AzureActiveDirectoryService { id: token.sessionId, refreshToken: token.refreshToken, scope: token.scope, - accountName: token.accountName + account: token.account }; }); @@ -196,7 +208,7 @@ export class AzureActiveDirectoryService { return { id: token.sessionId, getAccessToken: () => this.resolveAccessToken(token), - accountName: token.accountName, + account: token.account, scopes: token.scope.split(' ') }; } @@ -220,8 +232,6 @@ export class AzureActiveDirectoryService { } catch (e) { throw new Error('Unavailable due to network problems'); } - - throw new Error('Unavailable due to network problems'); } private getTokenClaims(accessToken: string): ITokenClaims { @@ -416,7 +426,10 @@ export class AzureActiveDirectoryService { refreshToken: json.refresh_token, scope, sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`, - accountName: claims.email || claims.unique_name || 'user@example.com' + account: { + displayName: claims.email || claims.unique_name || 'user@example.com', + id: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}` + } }; } diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts index 4ed2955794..7833166849 100644 --- a/extensions/vscode-account/src/extension.ts +++ b/extensions/vscode-account/src/extension.ts @@ -6,12 +6,15 @@ import * as vscode from 'vscode'; import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper'; import * as nls from 'vscode-nls'; +import TelemetryReporter from 'vscode-extension-telemetry'; const localize = nls.loadMessageBundle(); export const DEFAULT_SCOPES = 'https://management.core.windows.net/.default offline_access'; export async function activate(context: vscode.ExtensionContext) { + const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string }; + const telemetryReporter = new TelemetryReporter(name, version, aiKey); const loginService = new AzureActiveDirectoryService(); @@ -24,18 +27,24 @@ export async function activate(context: vscode.ExtensionContext) { getSessions: () => Promise.resolve(loginService.sessions), login: async (scopes: string[]) => { try { + telemetryReporter.sendTelemetryEvent('login'); await loginService.login(scopes.sort().join(' ')); const session = loginService.sessions[loginService.sessions.length - 1]; onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return loginService.sessions[0]!; } catch (e) { + telemetryReporter.sendTelemetryEvent('loginFailed'); throw e; } }, logout: async (id: string) => { - await loginService.logout(id); - onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); - vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out.")); + try { + telemetryReporter.sendTelemetryEvent('logout'); + await loginService.logout(id); + onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); + } catch (e) { + telemetryReporter.sendTelemetryEvent('logoutFailed'); + } } })); @@ -60,7 +69,7 @@ export async function activate(context: vscode.ExtensionContext) { const selectedSession = await vscode.window.showQuickPick(sessions.map(session => { return { id: session.id, - label: session.accountName + label: session.account.displayName }; })); diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/vscode-account/src/keychain.ts index 73ab288f5f..5cb90812b4 100644 --- a/extensions/vscode-account/src/keychain.ts +++ b/extensions/vscode-account/src/keychain.ts @@ -28,7 +28,7 @@ export type Keytar = { deletePassword: typeof keytarType['deletePassword']; }; -const SERVICE_ID = `${vscode.env.uriScheme}-vscode.login`; +const SERVICE_ID = `${vscode.env.uriScheme}-microsoft.login`; const ACCOUNT_ID = 'account'; export class Keychain { @@ -43,13 +43,38 @@ export class Keychain { this.keytar = keytar; } + // TODO remove, temporary migration + async migrateToken(): Promise { + const oldServiceId = `${vscode.env.uriScheme}-vscode.login`; + try { + const data = await this.keytar.getPassword(oldServiceId, ACCOUNT_ID); + if (data) { + Logger.info('Migrating token...'); + this.setToken(data); + await this.keytar.deletePassword(oldServiceId, ACCOUNT_ID); + Logger.info('Migration successful'); + } + } catch (e) { + Logger.error(`Migrating token failed: ${e}`); + } + } + + async setToken(token: string): Promise { try { Logger.trace('Writing to keychain', token); return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token); } catch (e) { - // Ignore Logger.error(`Setting token failed: ${e}`); + + // Temporary fix for #94005 + // This happens when processes write simulatenously to the keychain, most + // likely when trying to refresh the token. Ignore the error since additional + // writes after the first one do not matter. Should actually be fixed upstream. + if (e.message === 'The specified item already exists in the keychain.') { + return; + } + const troubleshooting = localize('troubleshooting', "Troubleshooting Guide"); const result = await vscode.window.showErrorMessage(localize('keychainWriteError', "Writing login information to the keychain failed with error '{0}'.", e.message), troubleshooting); if (result === troubleshooting) { diff --git a/extensions/vscode-account/src/logger.ts b/extensions/vscode-account/src/logger.ts index c5dd1235bd..de4a8ee3c7 100644 --- a/extensions/vscode-account/src/logger.ts +++ b/extensions/vscode-account/src/logger.ts @@ -17,7 +17,7 @@ class Log { private level: Level; constructor() { - this.output = vscode.window.createOutputChannel('Account'); + this.output = vscode.window.createOutputChannel('Microsoft Authentication'); this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info; vscode.workspace.onDidChangeConfiguration(e => { if (e.affectsConfiguration('microsoftAccount.logLevel')) { diff --git a/extensions/vscode-account/yarn.lock b/extensions/vscode-account/yarn.lock index 1506f62c87..a09801af2c 100644 --- a/extensions/vscode-account/yarn.lock +++ b/extensions/vscode-account/yarn.lock @@ -52,6 +52,15 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== + dependencies: + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.6" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -170,6 +179,18 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +diagnostic-channel-publishers@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -645,6 +666,13 @@ uuid@^3.3.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== + dependencies: + applicationinsights "1.0.8" + vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" @@ -666,3 +694,8 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +zone.js@0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/package.json b/package.json index f8d746bdd0..7c8fafd18f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,9 @@ "postinstall": "node build/npm/postinstall.js", "compile": "gulp compile --max_old_space_size=4095", "watch": "gulp watch --max_old_space_size=4095", + "watchd": "deemon yarn watch", + "kill-watchd": "deemon --kill yarn watch", + "restart-watchd": "deemon --restart yarn watch", "watch-client": "gulp watch-client --max_old_space_size=4095", "mocha": "mocha test/unit/node/all.js --delay", "precommit": "node build/gulpfile.hygiene.js", @@ -56,19 +59,19 @@ "iconv-lite": "0.5.0", "jquery": "3.4.0", "jschardet": "2.1.1", - "keytar": "github:rmacfarlane/node-keytar#334424bd26414923782f144110f4beda19168d24", + "keytar": "^5.5.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", "native-keymap": "2.1.1", "native-watchdog": "1.3.0", "ng2-charts": "^1.6.0", - "node-pty": "0.10.0-beta7", + "node-pty": "0.10.0-beta8", "onigasm-umd": "2.2.5", "plotly.js-dist-min": "^1.53.0", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", "sanitize-html": "^1.19.1", - "semver-umd": "^5.5.5", + "semver-umd": "^5.5.6", "slickgrid": "github:anthonydresser/SlickGrid#2.3.33", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", @@ -76,13 +79,13 @@ "vscode-nsfw": "1.2.8", "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.8", - "vscode-sqlite3": "4.0.9", + "vscode-sqlite3": "4.0.10", "vscode-textmate": "4.4.0", - "xterm": "4.5.0-beta.4", - "xterm-addon-search": "0.5.0", - "xterm-addon-unicode11": "0.1.1", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0", + "xterm": "4.6.0-beta.15", + "xterm-addon-search": "0.6.0", + "xterm-addon-unicode11": "0.2.0-beta.2", + "xterm-addon-web-links": "0.3.0", + "xterm-addon-webgl": "0.7.0-beta.6", "yauzl": "^2.9.2", "yazl": "^2.4.3", "zone.js": "^0.8.4" @@ -122,7 +125,8 @@ "cson-parser": "^1.3.3", "css-loader": "^3.2.0", "debounce": "^1.0.0", - "electron": "7.1.11", + "deemon": "^1.4.0", + "electron": "7.2.2", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", @@ -179,7 +183,7 @@ "temp-write": "^3.4.0", "ts-loader": "^4.4.2", "typemoq": "^0.3.2", - "typescript": "^3.9.0-dev.20200327", + "typescript": "^3.9.0-dev.20200420", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/remote/package.json b/remote/package.json index 141dc0d7fc..c13dcc10af 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,23 +26,23 @@ "minimist": "^1.2.5", "native-watchdog": "1.3.0", "ng2-charts": "^1.6.0", - "node-pty": "0.10.0-beta7", + "node-pty": "0.10.0-beta8", "onigasm-umd": "2.2.5", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", "sanitize-html": "^1.19.1", - "semver-umd": "^5.5.5", + "semver-umd": "^5.5.6", "slickgrid": "github:anthonydresser/SlickGrid#2.3.33", "spdlog": "^0.11.1", "vscode-nsfw": "1.2.8", "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.8", "vscode-textmate": "4.4.0", - "xterm": "4.5.0-beta.4", - "xterm-addon-search": "0.5.0", - "xterm-addon-unicode11": "0.1.1", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0", + "xterm": "4.6.0-beta.15", + "xterm-addon-search": "0.6.0", + "xterm-addon-unicode11": "0.2.0-beta.2", + "xterm-addon-web-links": "0.3.0", + "xterm-addon-webgl": "0.7.0-beta.6", "yauzl": "^2.9.2", "yazl": "^2.4.3", "zone.js": "^0.8.4" diff --git a/remote/web/package.json b/remote/web/package.json index 62cd0c6e26..37e5402fe8 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -20,14 +20,14 @@ "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", "sanitize-html": "^1.19.1", - "semver-umd": "^5.5.5", + "semver-umd": "^5.5.6", "slickgrid": "github:anthonydresser/SlickGrid#2.3.33", "vscode-textmate": "4.4.0", - "xterm": "4.5.0-beta.4", - "xterm-addon-search": "0.5.0", - "xterm-addon-unicode11": "0.1.1", - "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.5.0", + "xterm": "4.6.0-beta.15", + "xterm-addon-search": "0.6.0", + "xterm-addon-unicode11": "0.2.0-beta.2", + "xterm-addon-web-links": "0.3.0", + "xterm-addon-webgl": "0.7.0-beta.6", "zone.js": "^0.8.4" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index f20d87ae48..fe6d1966c9 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -302,10 +302,10 @@ sanitize-html@^1.19.1: srcset "^1.0.0" xtend "^4.0.1" -semver-umd@^5.5.5: - version "5.5.5" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" - integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== +semver-umd@^5.5.6: + version "5.5.6" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228" + integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw== "slickgrid@github:anthonydresser/SlickGrid#2.3.33": version "2.3.33" @@ -367,30 +367,30 @@ xtend@^4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-search@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.5.0.tgz#cd3a2f8056084c28e236d4e732da37682010bcc2" - integrity sha512-zLVqVTrg5w2nk9fRj3UuVKCPo/dmFe/cLf3EM9Is5Dm6cgOoXmeo9eq2KgD8A0gquAflTFTf0ya2NaFmShHwyg== +xterm-addon-search@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.6.0.tgz#542cc2c35e83e7332ce1982b65ad218ee769836c" + integrity sha512-k3EsZzUptCXygHFP5rQuCBdWWkI/ZNuX3pDSOVdxPV9jB7U5Aha9guTIZoMP7FIjL8jce+ClQs6q7VINcRV1+w== -xterm-addon-unicode11@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.1.1.tgz#b209ef137db38096f68636af4ef4d0c0acba85ad" - integrity sha512-z6vJTL+dpNljwAYzYoyDjJP8A2XjZuEosl0sRa+FGRf3jEyEVWquDM53MfUd1ztVdAPQ839qR6eYK1BXV04Bhw== +xterm-addon-unicode11@0.2.0-beta.2: + version "0.2.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.2.tgz#2a13ba5b08fdb1005be241816c4e3302674db4af" + integrity sha512-Y047mnIWrAj65TpStdyPYoPeDTX4en+XX4Y90KuQB3cW2xIyZj25NSVV9BZdqzSb7gk9M6KBvIcm8chj7S2N8Q== -xterm-addon-web-links@0.2.1: - version "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-addon-web-links@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.3.0.tgz#88affe9235c928b41bab660a65330f46d91c940e" + integrity sha512-vGXiIDqNMyxK5S1IzOjDqcgeQrrv7TDcSHiOeCNAoWCI2f+Rap9d18gjgnMKPyR+AbG0KoKnaKA6Dc1du1vs5A== -xterm-addon-webgl@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e" - integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA== +xterm-addon-webgl@0.7.0-beta.6: + version "0.7.0-beta.6" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.6.tgz#340d728bae7456a1d67a7cb8996dfd51d96721b0" + integrity sha512-IoR0hPtG5qBrcLG1B7GSzo4W2hYocP8UgG5LlyXkEkT/0BqVcGnICgR8Ck7EfMmU8ci4jNFiHYjK/Bgc4m2S4g== -xterm@4.5.0-beta.4: - version "4.5.0-beta.4" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92" - integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew== +xterm@4.6.0-beta.15: + version "4.6.0-beta.15" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.15.tgz#93a0cb1ff047a452b03070499e2ccd0ba53e0678" + integrity sha512-5G+3QSM/GKN/Tdq/IIU7FDivzh0eXsv3sNmZFDdtvNggnu2K56l4N5P0KZo0HdFztDfTyW/FLfqsPAwhrK4Khg== zone.js@^0.8.4: version "0.8.29" diff --git a/remote/yarn.lock b/remote/yarn.lock index 5bc4e357ee..ef524e3759 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -498,10 +498,10 @@ node-addon-api@1.6.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-pty@0.10.0-beta7: - version "0.10.0-beta7" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta7.tgz#7e383b2d1fe2f34509b57187f5a9a6ff90c46111" - integrity sha512-oC2VyIz9YaIvv6lWjAPZbUzmhLW1ouFmxOogNRNQrKeUzUi2yM/QRmybs+dW/Mhd3V89Yh61Ml0J5yuWiMIBbw== +node-pty@0.10.0-beta8: + version "0.10.0-beta8" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta8.tgz#f4aa56c71a794f4580373a3030dfebd25240c6db" + integrity sha512-Ul/hLsadC0SvvShxpne+kq2ebSMcitewlNhrwoXXBvFdCqxJt7Ai1AgMhH7AKBUp06uBeYXThJ2ihTszrkdnYw== dependencies: nan "^2.14.0" @@ -600,10 +600,10 @@ sanitize-html@^1.19.1: srcset "^1.0.0" xtend "^4.0.1" -semver-umd@^5.5.5: - version "5.5.5" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" - integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== +semver-umd@^5.5.6: + version "5.5.6" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228" + integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw== semver@^5.3.0: version "5.6.0" @@ -749,30 +749,30 @@ xtend@^4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-search@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.5.0.tgz#cd3a2f8056084c28e236d4e732da37682010bcc2" - integrity sha512-zLVqVTrg5w2nk9fRj3UuVKCPo/dmFe/cLf3EM9Is5Dm6cgOoXmeo9eq2KgD8A0gquAflTFTf0ya2NaFmShHwyg== +xterm-addon-search@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.6.0.tgz#542cc2c35e83e7332ce1982b65ad218ee769836c" + integrity sha512-k3EsZzUptCXygHFP5rQuCBdWWkI/ZNuX3pDSOVdxPV9jB7U5Aha9guTIZoMP7FIjL8jce+ClQs6q7VINcRV1+w== -xterm-addon-unicode11@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.1.1.tgz#b209ef137db38096f68636af4ef4d0c0acba85ad" - integrity sha512-z6vJTL+dpNljwAYzYoyDjJP8A2XjZuEosl0sRa+FGRf3jEyEVWquDM53MfUd1ztVdAPQ839qR6eYK1BXV04Bhw== +xterm-addon-unicode11@0.2.0-beta.2: + version "0.2.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0-beta.2.tgz#2a13ba5b08fdb1005be241816c4e3302674db4af" + integrity sha512-Y047mnIWrAj65TpStdyPYoPeDTX4en+XX4Y90KuQB3cW2xIyZj25NSVV9BZdqzSb7gk9M6KBvIcm8chj7S2N8Q== -xterm-addon-web-links@0.2.1: - version "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-addon-web-links@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.3.0.tgz#88affe9235c928b41bab660a65330f46d91c940e" + integrity sha512-vGXiIDqNMyxK5S1IzOjDqcgeQrrv7TDcSHiOeCNAoWCI2f+Rap9d18gjgnMKPyR+AbG0KoKnaKA6Dc1du1vs5A== -xterm-addon-webgl@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e" - integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA== +xterm-addon-webgl@0.7.0-beta.6: + version "0.7.0-beta.6" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0-beta.6.tgz#340d728bae7456a1d67a7cb8996dfd51d96721b0" + integrity sha512-IoR0hPtG5qBrcLG1B7GSzo4W2hYocP8UgG5LlyXkEkT/0BqVcGnICgR8Ck7EfMmU8ci4jNFiHYjK/Bgc4m2S4g== -xterm@4.5.0-beta.4: - version "4.5.0-beta.4" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92" - integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew== +xterm@4.6.0-beta.15: + version "4.6.0-beta.15" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.6.0-beta.15.tgz#93a0cb1ff047a452b03070499e2ccd0ba53e0678" + integrity sha512-5G+3QSM/GKN/Tdq/IIU7FDivzh0eXsv3sNmZFDdtvNggnu2K56l4N5P0KZo0HdFztDfTyW/FLfqsPAwhrK4Khg== yauzl@^2.9.2: version "2.10.0" diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat index 56c3756720..5c9cdd1442 100644 --- a/scripts/code-cli.bat +++ b/scripts/code-cli.bat @@ -30,7 +30,8 @@ if not exist out yarn compile set ELECTRON_RUN_AS_NODE=1 set NODE_ENV=development set VSCODE_DEV=1 -set ELECTRON_DEFAULT_ERROR_MODE=1 +set ELECTRON_ENABLE_SECURITY_WARNINGS=1 +REM set ELECTRON_DEFAULT_ERROR_MODE=1 TODO@ben to investigate if this helps with builds reporting stacks if renderer crashes set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh index e4fa552e64..98d117c199 100755 --- a/scripts/code-cli.sh +++ b/scripts/code-cli.sh @@ -39,6 +39,7 @@ function code() { ELECTRON_RUN_AS_NODE=1 \ NODE_ENV=development \ VSCODE_DEV=1 \ + ELECTRON_ENABLE_SECURITY_WARNINGS=1 \ ELECTRON_ENABLE_LOGGING=1 \ ELECTRON_ENABLE_STACK_DUMPING=1 \ "$CODE" --inspect=5874 "$ROOT/out/cli.js" . "$@" diff --git a/scripts/code.bat b/scripts/code.bat index 770d37b7ae..ca1853e3c5 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -28,8 +28,9 @@ if not exist out yarn compile :: Configuration set NODE_ENV=development set VSCODE_DEV=1 +set ELECTRON_ENABLE_SECURITY_WARNINGS=1 set VSCODE_CLI=1 -set ELECTRON_DEFAULT_ERROR_MODE=1 +REM set ELECTRON_DEFAULT_ERROR_MODE=1 TODO@ben to investigate if this helps with builds reporting stacks if renderer crashes set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 set VSCODE_LOGS= diff --git a/scripts/code.sh b/scripts/code.sh index 3bc09f54f4..634dea0419 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -44,6 +44,7 @@ function code() { # Configuration export NODE_ENV=development export VSCODE_DEV=1 + export ELECTRON_ENABLE_SECURITY_WARNINGS=1 export VSCODE_CLI=1 export ELECTRON_ENABLE_STACK_DUMPING=1 export ELECTRON_ENABLE_LOGGING=1 diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index 88f6d16e6b..9af27b2419 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri'; import { dirname, basename } from 'vs/base/common/resources'; import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; -import { WorkbenchAsyncDataTree, ResourceNavigator } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; import { localize } from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -416,9 +416,9 @@ export class CustomTreeView extends Disposable implements ITreeView { accessibilityProvider: { getAriaLabel(element: ITreeItem): string { return element.label ? element.label.label : ''; - } + }, + getWidgetAriaLabel: () => this.title }, - ariaLabel: this.title, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (item: ITreeItem) => { return item.label ? item.label.label : (item.resourceUri ? basename(URI.revive(item.resourceUri)) : undefined); @@ -451,7 +451,7 @@ export class CustomTreeView extends Disposable implements ITreeView { })); this.tree.setInput(this.root).then(() => this.updateContentAreas()); - const customTreeNavigator = ResourceNavigator.createTreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false }); + const customTreeNavigator = new TreeResourceNavigator(this.tree, { openOnFocus: false, openOnSelection: false }); this._register(customTreeNavigator); this._register(customTreeNavigator.onDidOpenResource(e => { if (!e.browserEvent) { diff --git a/src/sql/workbench/contrib/accounts/browser/accounts.contribution.ts b/src/sql/workbench/contrib/accounts/browser/accounts.contribution.ts index 5b4ff5ae44..393bab3e0f 100644 --- a/src/sql/workbench/contrib/accounts/browser/accounts.contribution.ts +++ b/src/sql/workbench/contrib/accounts/browser/accounts.contribution.ts @@ -28,7 +28,8 @@ class AccountsStatusBarContributions extends Disposable implements IWorkbenchCon this._register( this.statusbarService.addEntry({ command: 'workbench.actions.modal.linkedAccount', - text: '$(person-filled)' + text: '$(person-filled)', + ariaLabel: 'Accounts' }, 'status.accountList', localize('status.problems', "Problems"), diff --git a/src/sql/workbench/contrib/connection/browser/connectionStatus.ts b/src/sql/workbench/contrib/connection/browser/connectionStatus.ts index 4c4a09d613..1b2347378c 100644 --- a/src/sql/workbench/contrib/connection/browser/connectionStatus.ts +++ b/src/sql/workbench/contrib/connection/browser/connectionStatus.ts @@ -30,6 +30,7 @@ export class ConnectionStatusbarItem extends Disposable implements IWorkbenchCon this.statusItem = this._register( this.statusbarService.addEntry({ text: '', + ariaLabel: '' }, ConnectionStatusbarItem.ID, localize('status.connection.status', "Connection Status"), @@ -84,7 +85,7 @@ export class ConnectionStatusbarItem extends Disposable implements IWorkbenchCon } this.statusItem.update({ - text, tooltip + text, ariaLabel: text, tooltip }); } } diff --git a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts index afd7e27c8d..430a9d0816 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts @@ -105,7 +105,7 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer { @IContextKeyService private contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); } create(parent: HTMLElement): void { diff --git a/src/sql/workbench/contrib/editData/browser/editDataEditor.ts b/src/sql/workbench/contrib/editData/browser/editDataEditor.ts index 51f5c7b1b3..1be8eca27b 100644 --- a/src/sql/workbench/contrib/editData/browser/editDataEditor.ts +++ b/src/sql/workbench/contrib/editData/browser/editDataEditor.ts @@ -88,11 +88,13 @@ export class EditDataEditor extends BaseEditor { } if (_editorService) { - _editorService.overrideOpenEditor((editor, options, group) => { - if (this.isVisible() && (editor !== this.input || group !== this.group)) { - this.saveEditorViewState(); + _editorService.overrideOpenEditor({ + open: (editor, options, group) => { + if (this.isVisible() && (editor !== this.input || group !== this.group)) { + this.saveEditorViewState(); + } + return {}; } - return {}; }); } } diff --git a/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts b/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts index fb83668915..a2fc6388db 100644 --- a/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts +++ b/src/sql/workbench/contrib/editorReplacement/common/editorReplacerContribution.ts @@ -27,7 +27,9 @@ export class EditorReplacementContribution implements IWorkbenchContribution { @IEditorService private readonly editorService: IEditorService, @IModeService private readonly modeService: IModeService ) { - this.editorOpeningListener = this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group)); + this.editorOpeningListener = this.editorService.overrideOpenEditor({ + open: (editor, options, group) => this.onEditorOpening(editor, options, group) + }); } private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined { diff --git a/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts b/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts index c4a84720d5..d590de6758 100644 --- a/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts +++ b/src/sql/workbench/contrib/editorReplacement/test/browser/editorReplacerContribution.test.ts @@ -194,7 +194,7 @@ class MockEditorService extends TestEditorService { fireOpenEditor(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup) { for (const handler of this.overridenOpens) { let response: IOpenEditorOverride | undefined; - if (response = handler(editor, options, group)) { + if (response = handler.open(editor, options, group)) { return response; } } diff --git a/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts b/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts new file mode 100644 index 0000000000..ab29ef6a47 --- /dev/null +++ b/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { localize } from 'vs/nls'; +import { IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { visualizerExtensions } from 'sql/workbench/contrib/extensions/common/constants'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; +import { InstallRecommendedExtensionsByScenarioAction, ShowRecommendedExtensionsByScenarioAction } from 'sql/workbench/contrib/extensions/browser/extensionsActions'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; + +const choiceNever = localize('neverShowAgain', "Don't Show Again"); + +export class ScenarioRecommendations extends ExtensionRecommendations { + + readonly _recommendations: ExtensionRecommendation[] = []; + get recommendations(): ReadonlyArray { return this._recommendations; } + + constructor( + isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, + @IProductService private readonly productService: IProductService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @INotificationService notificationService: INotificationService, + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService, + @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + ) { + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + + // this._recommendations = productService.recommendedExtensionsByScenario.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' })); + } + + protected async doActivate(): Promise { + return; + } + + // {{SQL CARBON EDIT}} + promptRecommendedExtensionsByScenario(scenarioType: string): void { + const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType; + + if (this.storageService.getBoolean(storageKey, StorageScope.GLOBAL, false)) { + return; + } + + const visualizerExtensionNotificationService = 'VisualizerExtensionNotificationService'; + + let recommendationMessage = localize('ExtensionsRecommended', "Azure Data Studio has extension recommendations."); + if (scenarioType === visualizerExtensions) { + recommendationMessage = localize('VisualizerExtensionsRecommended', "Azure Data Studio has extension recommendations for data visualization.\nOnce installed, you can select the Visualizer icon to visualize your query results."); + } + Promise.all([this.getRecommendedExtensionsByScenario(scenarioType), this.extensionManagementService.getInstalled(ExtensionType.User)]).then(([recommendations, localExtensions]) => { + if (!recommendations.every(rec => { return localExtensions.findIndex(local => local.identifier.id.toLocaleLowerCase() === rec.extensionId.toLocaleLowerCase()) !== -1; })) { + return new Promise(c => { + this.notificationService.prompt( + Severity.Info, + recommendationMessage, + [{ + label: localize('installAll', "Install All"), + run: () => { + this.adsTelemetryService.sendActionEvent( + TelemetryKeys.TelemetryView.ExtensionRecommendationDialog, + TelemetryKeys.TelemetryAction.Click, + 'InstallButton', + visualizerExtensionNotificationService + ); + const installAllAction = this.instantiationService.createInstance(InstallRecommendedExtensionsByScenarioAction, scenarioType, recommendations); + installAllAction.run(); + installAllAction.dispose(); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: () => { + this.adsTelemetryService.sendActionEvent( + TelemetryKeys.TelemetryView.ExtensionRecommendationDialog, + TelemetryKeys.TelemetryAction.Click, + 'ShowRecommendationsButton', + visualizerExtensionNotificationService + ); + const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsByScenarioAction, scenarioType); + showAction.run(); + showAction.dispose(); + c(undefined); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + this.adsTelemetryService.sendActionEvent( + TelemetryKeys.TelemetryView.ExtensionRecommendationDialog, + TelemetryKeys.TelemetryAction.Click, + 'NeverShowAgainButton', + visualizerExtensionNotificationService + ); + this.storageService.store(storageKey, true, StorageScope.GLOBAL); + c(undefined); + } + }], + { + sticky: true, + onCancel: () => { + this.adsTelemetryService.sendActionEvent( + TelemetryKeys.TelemetryView.ExtensionRecommendationDialog, + TelemetryKeys.TelemetryAction.Click, + 'CancelButton', + visualizerExtensionNotificationService + ); + c(undefined); + } + } + ); + }); + } else { + return Promise.resolve(); + } + }); + } + + getRecommendedExtensionsByScenario(scenarioType: string): Promise { + if (!scenarioType) { + return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.'))); + } + return Promise.resolve((this.productService.recommendedExtensionsByScenario[scenarioType] || []) + .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) + .map(extensionId => ({ extensionId, sources: ['application'] }))); + } +} diff --git a/src/sql/workbench/contrib/extensions/browser/staticRecommendations.ts b/src/sql/workbench/contrib/extensions/browser/staticRecommendations.ts new file mode 100644 index 0000000000..632003c766 --- /dev/null +++ b/src/sql/workbench/contrib/extensions/browser/staticRecommendations.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { localize } from 'vs/nls'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; + +export class StaticRecommendations extends ExtensionRecommendations { + + readonly _recommendations: ExtensionRecommendation[] = []; + get recommendations(): ReadonlyArray { return this._recommendations; } + + constructor( + isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, + @IProductService productService: IProductService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @INotificationService notificationService: INotificationService, + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService + ) { + super(isExtensionAllowedToBeRecommended, instantiationService, configurationService, notificationService, telemetryService, storageService, storageKeysSyncRegistryService); + + this._recommendations = productService.recommendedExtensions.map(r => ({ extensionId: r, reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }, source: 'application' })); + } + + protected async doActivate(): Promise { + return; + } +} diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts index cba6495bf7..f975d47c75 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts @@ -365,7 +365,8 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle searchScope: true, matchesPosition: false, matchesCount: false, - currentMatch: false + currentMatch: false, + loop: true }; this._notebookModel.cells.forEach(cell => { this._register(cell.onCellModeChanged((state) => { @@ -450,7 +451,8 @@ export class NotebookEditor extends BaseEditor implements IFindNotebookControlle searchScope: false, matchesPosition: false, matchesCount: false, - currentMatch: false + currentMatch: false, + loop: true }; this._onFindStateChange(changeEvent).catch(e => { onUnexpectedError(e); }); } diff --git a/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts b/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts index dd62a57759..b5b29cb09f 100644 --- a/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts +++ b/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts @@ -172,11 +172,13 @@ export class ProfilerEditor extends BaseEditor { this._profilerEditorContextKey = CONTEXT_PROFILER_EDITOR.bindTo(this._contextKeyService); if (editorService) { - editorService.overrideOpenEditor((editor, options, group) => { - if (this.isVisible() && (editor !== this.input || group !== this.group)) { - this.saveEditorViewState(); + editorService.overrideOpenEditor({ + open: (editor, options, group) => { + if (this.isVisible() && (editor !== this.input || group !== this.group)) { + this.saveEditorViewState(); + } + return {}; } - return {}; }); } } @@ -502,7 +504,8 @@ export class ProfilerEditor extends BaseEditor { seedSearchStringFromSelection: (controller.getState().searchString.length === 0), shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: true }); } } else { diff --git a/src/sql/workbench/contrib/profiler/browser/profilerTableEditor.ts b/src/sql/workbench/contrib/profiler/browser/profilerTableEditor.ts index e04062b61d..d82f5554e7 100644 --- a/src/sql/workbench/contrib/profiler/browser/profilerTableEditor.ts +++ b/src/sql/workbench/contrib/profiler/browser/profilerTableEditor.ts @@ -288,7 +288,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll : localize('ProfilerTableEditor.eventCount', "Events: {0}", this._input.data.getLength()); this._disposeStatusbarItem(); - this._statusbarItem = this._statusbarService.addEntry({ text: message }, 'status.eventCount', localize('status.eventCount', "Event Count"), StatusbarAlignment.RIGHT); + this._statusbarItem = this._statusbarService.addEntry({ text: message, ariaLabel: message }, 'status.eventCount', localize('status.eventCount', "Event Count"), StatusbarAlignment.RIGHT); } } diff --git a/src/sql/workbench/contrib/query/browser/flavorStatus.ts b/src/sql/workbench/contrib/query/browser/flavorStatus.ts index 1a2a768d74..421a5b2d50 100644 --- a/src/sql/workbench/contrib/query/browser/flavorStatus.ts +++ b/src/sql/workbench/contrib/query/browser/flavorStatus.ts @@ -74,8 +74,8 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont this.statusItem = this._register( this.statusbarService.addEntry({ text: nls.localize('changeProvider', "Change SQL language provider"), + ariaLabel: nls.localize('changeProvider', "Change SQL language provider"), command: 'sql.action.editor.changeProvider' - }, SqlFlavorStatusbarItem.ID, nls.localize('status.query.flavor', "SQL Language Flavor"), @@ -161,6 +161,7 @@ export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchCont private updateFlavorElement(text: string): void { const props: IStatusbarEntry = { text, + ariaLabel: text, command: 'sql.action.editor.changeProvider' }; diff --git a/src/sql/workbench/contrib/query/browser/statusBarItems.ts b/src/sql/workbench/contrib/query/browser/statusBarItems.ts index 3cbe7229c7..31ab2cacbe 100644 --- a/src/sql/workbench/contrib/query/browser/statusBarItems.ts +++ b/src/sql/workbench/contrib/query/browser/statusBarItems.ts @@ -33,6 +33,7 @@ export class TimeElapsedStatusBarContributions extends Disposable implements IWo this.statusItem = this._register( this.statusbarService.addEntry({ text: '', + ariaLabel: '' }, TimeElapsedStatusBarContributions.ID, localize('status.query.timeElapsed', "Time Elapsed"), @@ -89,20 +90,26 @@ export class TimeElapsedStatusBarContributions extends Disposable implements IWo if (runner.isExecuting) { this.intervalTimer.cancelAndSet(() => { const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; + const timeString = parseNumAsTimeString(value, false); this.statusItem.update({ - text: parseNumAsTimeString(value, false) + text: timeString, + ariaLabel: timeString }); }, 1000); const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; + const timeString = parseNumAsTimeString(value, false); this.statusItem.update({ - text: parseNumAsTimeString(value, false) + text: timeString, + ariaLabel: timeString }); } else { const value = runner.queryStartTime && runner.queryEndTime ? runner.queryEndTime.getTime() - runner.queryStartTime.getTime() : 0; + const timeString = parseNumAsTimeString(value, false); this.statusItem.update({ - text: parseNumAsTimeString(value, false) + text: timeString, + ariaLabel: timeString }); } this.show(); @@ -126,6 +133,7 @@ export class RowCountStatusBarContributions extends Disposable implements IWorkb this.statusItem = this._register( this.statusbarService.addEntry({ text: '', + ariaLabel: '' }, RowCountStatusBarContributions.ID, localize('status.query.rowCount', "Row Count"), @@ -191,7 +199,7 @@ export class RowCountStatusBarContributions extends Disposable implements IWorkb }, 0); }, 0); const text = localize('rowCount', "{0} rows", rowCount.toLocaleString()); - this.statusItem.update({ text }); + this.statusItem.update({ text, ariaLabel: text }); this.show(); } } @@ -211,6 +219,7 @@ export class QueryStatusStatusBarContributions extends Disposable implements IWo this._register( this.statusbarService.addEntry({ text: localize('query.status.executing', "Executing query..."), + ariaLabel: localize('query.status.executing', "Executing query...") }, QueryStatusStatusBarContributions.ID, localize('status.query.status', "Execution Status"), diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 59a686624b..f6984aea43 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -822,6 +822,7 @@ export const EventType = { MOUSE_OUT: 'mouseout', MOUSE_ENTER: 'mouseenter', MOUSE_LEAVE: 'mouseleave', + MOUSE_WHEEL: browser.isEdge ? 'mousewheel' : 'wheel', POINTER_UP: 'pointerup', POINTER_DOWN: 'pointerdown', POINTER_MOVE: 'pointermove', diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index d9060f4354..7b73ba0503 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -197,8 +197,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const renderedMarkdown = marked.parse( markdown.supportThemeIcons - ? markdownEscapeEscapedCodicons(markdown.value) - : markdown.value, + ? markdownEscapeEscapedCodicons(markdown.value || '') + : (markdown.value || ''), markedOptions ); diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index a20722bddb..378002ec95 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -80,15 +80,11 @@ export class StandardMouseEvent implements IMouseEvent { } public preventDefault(): void { - if (this.browserEvent.preventDefault) { - this.browserEvent.preventDefault(); - } + this.browserEvent.preventDefault(); } public stopPropagation(): void { - if (this.browserEvent.stopPropagation) { - this.browserEvent.stopPropagation(); - } + this.browserEvent.stopPropagation(); } } @@ -208,17 +204,13 @@ export class StandardWheelEvent { public preventDefault(): void { if (this.browserEvent) { - if (this.browserEvent.preventDefault) { - this.browserEvent.preventDefault(); - } + this.browserEvent.preventDefault(); } } public stopPropagation(): void { if (this.browserEvent) { - if (this.browserEvent.stopPropagation) { - this.browserEvent.stopPropagation(); - } + this.browserEvent.stopPropagation(); } } } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index b88982703d..c5e72dded2 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -136,7 +136,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { if (platform.isMacintosh) { // macOS: allow to trigger the button when holding Ctrl+key and pressing the - // main mouse button. This is for scenarios where e.g. some interaction forces + // main mouse button. This is for scenarios where e.g. some interaction forces // the Ctrl+key to be pressed and hold but the user still wants to interact // with the actions (for example quick access in quick navigation mode). this._register(DOM.addDisposableListener(element, DOM.EventType.CONTEXT_MENU, e => { @@ -276,7 +276,6 @@ export class ActionViewItem extends BaseActionViewItem { this.label = DOM.append(this.element, DOM.$('a.action-label')); } - if (this.label) { if (this._action.id === Separator.ID) { this.label.setAttribute('role', 'presentation'); // A separator is a presentation item diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index f03afaf0e8..1d1f0b0dd4 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./aria'; -import * as nls from 'vs/nls'; import { isMacintosh } from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; @@ -23,7 +22,8 @@ export function setARIAContainer(parent: HTMLElement) { statusContainer = document.createElement('div'); statusContainer.className = 'monaco-status'; - statusContainer.setAttribute('role', 'status'); + statusContainer.setAttribute('role', 'complementary'); + statusContainer.setAttribute('aria-live', 'polite'); statusContainer.setAttribute('aria-atomic', 'true'); ariaContainer.appendChild(statusContainer); @@ -33,51 +33,30 @@ export function setARIAContainer(parent: HTMLElement) { /** * Given the provided message, will make sure that it is read as alert to screen readers. */ -export function alert(msg: string, disableRepeat?: boolean): void { - insertMessage(alertContainer, msg, disableRepeat); +export function alert(msg: string): void { + insertMessage(alertContainer, msg); } /** * Given the provided message, will make sure that it is read as status to screen readers. */ -export function status(msg: string, disableRepeat?: boolean): void { +export function status(msg: string): void { if (isMacintosh) { - alert(msg, disableRepeat); // VoiceOver does not seem to support status role + alert(msg); // VoiceOver does not seem to support status role } else { - insertMessage(statusContainer, msg, disableRepeat); + insertMessage(statusContainer, msg); } } -let repeatedTimes = 0; -let prevText: string | undefined = undefined; -function insertMessage(target: HTMLElement, msg: string, disableRepeat?: boolean): void { +function insertMessage(target: HTMLElement, msg: string): void { if (!ariaContainer) { return; } - // If the same message should be inserted that is already present, a screen reader would - // not announce this message because it matches the previous one. As a workaround, we - // alter the message with the number of occurences unless this is explicitly disabled - // via the disableRepeat flag. - if (!disableRepeat) { - if (prevText === msg) { - repeatedTimes++; - } else { - prevText = msg; - repeatedTimes = 0; - } - - switch (repeatedTimes) { - case 0: break; - case 1: msg = nls.localize('repeated', "{0} (occurred again)", msg); break; - default: msg = nls.localize('repeatedNtimes', "{0} (occurred {1} times)", msg, repeatedTimes); break; - } - } - dom.clearNode(target); target.textContent = msg; // See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/ target.style.visibility = 'hidden'; target.style.visibility = 'visible'; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 4ccd6ad7b2..b3b85d77e9 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -25,7 +25,7 @@ outline: none; } -.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-chevron-right { +.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator { color: inherit; } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index f8828fa3f0..7aefef3872 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -11,6 +11,7 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; import 'vs/css!./breadcrumbsWidget'; export abstract class BreadcrumbsItem { @@ -55,6 +56,8 @@ export interface IBreadcrumbsItemEvent { payload: any; } +const breadcrumbSeparatorIcon = registerIcon('breadcrumb-separator', Codicon.chevronRight); + export class BreadcrumbsWidget { private readonly _disposables = new DisposableStore(); @@ -336,7 +339,7 @@ export class BreadcrumbsWidget { container.tabIndex = -1; container.setAttribute('role', 'listitem'); dom.addClasses(container, 'monaco-breadcrumb-item'); - const iconContainer = dom.$('.codicon.codicon-chevron-right'); + const iconContainer = dom.$(breadcrumbSeparatorIcon.cssSelector); container.appendChild(iconContainer); } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 711f291ce8..61ad570210 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -10,12 +10,13 @@ import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import * as objects from 'vs/base/common/objects'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Codicon } from 'vs/base/common/codicons'; export interface ICheckboxOpts extends ICheckboxStyles { readonly actionClassName?: string; + readonly icon?: Codicon; readonly title: string; readonly isChecked: boolean; } @@ -93,13 +94,23 @@ export class Checkbox extends Widget { constructor(opts: ICheckboxOpts) { super(); - this._opts = objects.deepClone(opts); - objects.mixin(this._opts, defaultOpts, false); + this._opts = { ...defaultOpts, ...opts }; this._checked = this._opts.isChecked; + const classes = ['monaco-custom-checkbox']; + if (this._opts.icon) { + classes.push(this._opts.icon.classNames); + } else { + classes.push('codicon'); // todo@aeschli: remove once codicon fully adopted + } + if (this._opts.actionClassName) { + classes.push(this._opts.actionClassName); + } + classes.push(this._checked ? 'checked' : 'unchecked'); + this.domNode = document.createElement('div'); this.domNode.title = this._opts.title; - this.domNode.className = 'monaco-custom-checkbox codicon ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked'); + this.domNode.className = classes.join(' '); this.domNode.tabIndex = 0; this.domNode.setAttribute('role', 'checkbox'); this.domNode.setAttribute('aria-checked', String(this._checked)); @@ -192,7 +203,7 @@ export class SimpleCheckbox extends Widget { constructor(private title: string, private isChecked: boolean) { super(); - this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, actionClassName: 'monaco-simple-checkbox codicon-check' }); + this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, icon: Codicon.check, actionClassName: 'monaco-simple-checkbox' }); this.domNode = this.checkbox.domNode; diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css deleted file mode 100644 index 5d6b170186..0000000000 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ /dev/null @@ -1,427 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -@font-face { - font-family: "codicon"; - src: url("./codicon.ttf?a76e99e42eab7c1a55601640b708d820") format("truetype"); -} - -.codicon[class*='codicon-'] { - font: normal normal normal 16px/1 codicon; - display: inline-block; - text-decoration: none; - text-rendering: auto; - text-align: center; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - user-select: none; - -webkit-user-select: none; - -ms-user-select: none; -} - - -.codicon-add:before { content: "\ea60" } -.codicon-plus:before { content: "\ea60" } -.codicon-gist-new:before { content: "\ea60" } -.codicon-repo-create:before { content: "\ea60" } -.codicon-lightbulb:before { content: "\ea61" } -.codicon-light-bulb:before { content: "\ea61" } -.codicon-repo:before { content: "\ea62" } -.codicon-repo-delete:before { content: "\ea62" } -.codicon-gist-fork:before { content: "\ea63" } -.codicon-repo-forked:before { content: "\ea63" } -.codicon-git-pull-request:before { content: "\ea64" } -.codicon-git-pull-request-abandoned:before { content: "\ea64" } -.codicon-record-keys:before { content: "\ea65" } -.codicon-keyboard:before { content: "\ea65" } -.codicon-tag:before { content: "\ea66" } -.codicon-tag-add:before { content: "\ea66" } -.codicon-tag-remove:before { content: "\ea66" } -.codicon-person:before { content: "\ea67" } -.codicon-person-add:before { content: "\ea67" } -.codicon-person-follow:before { content: "\ea67" } -.codicon-person-outline:before { content: "\ea67" } -.codicon-person-filled:before { content: "\ea67" } -.codicon-git-branch:before { content: "\ea68" } -.codicon-git-branch-create:before { content: "\ea68" } -.codicon-git-branch-delete:before { content: "\ea68" } -.codicon-source-control:before { content: "\ea68" } -.codicon-mirror:before { content: "\ea69" } -.codicon-mirror-public:before { content: "\ea69" } -.codicon-star:before { content: "\ea6a" } -.codicon-star-add:before { content: "\ea6a" } -.codicon-star-delete:before { content: "\ea6a" } -.codicon-star-empty:before { content: "\ea6a" } -.codicon-comment:before { content: "\ea6b" } -.codicon-comment-add:before { content: "\ea6b" } -.codicon-alert:before { content: "\ea6c" } -.codicon-warning:before { content: "\ea6c" } -.codicon-search:before { content: "\ea6d" } -.codicon-search-save:before { content: "\ea6d" } -.codicon-log-out:before { content: "\ea6e" } -.codicon-sign-out:before { content: "\ea6e" } -.codicon-log-in:before { content: "\ea6f" } -.codicon-sign-in:before { content: "\ea6f" } -.codicon-eye:before { content: "\ea70" } -.codicon-eye-unwatch:before { content: "\ea70" } -.codicon-eye-watch:before { content: "\ea70" } -.codicon-circle-filled:before { content: "\ea71" } -.codicon-primitive-dot:before { content: "\ea71" } -.codicon-close-dirty:before { content: "\ea71" } -.codicon-debug-breakpoint:before { content: "\ea71" } -.codicon-debug-breakpoint-disabled:before { content: "\ea71" } -.codicon-debug-hint:before { content: "\ea71" } -.codicon-primitive-square:before { content: "\ea72" } -.codicon-edit:before { content: "\ea73" } -.codicon-pencil:before { content: "\ea73" } -.codicon-info:before { content: "\ea74" } -.codicon-issue-opened:before { content: "\ea74" } -.codicon-gist-private:before { content: "\ea75" } -.codicon-git-fork-private:before { content: "\ea75" } -.codicon-lock:before { content: "\ea75" } -.codicon-mirror-private:before { content: "\ea75" } -.codicon-close:before { content: "\ea76" } -.codicon-remove-close:before { content: "\ea76" } -.codicon-x:before { content: "\ea76" } -.codicon-repo-sync:before { content: "\ea77" } -.codicon-sync:before { content: "\ea77" } -.codicon-clone:before { content: "\ea78" } -.codicon-desktop-download:before { content: "\ea78" } -.codicon-beaker:before { content: "\ea79" } -.codicon-microscope:before { content: "\ea79" } -.codicon-vm:before { content: "\ea7a" } -.codicon-device-desktop:before { content: "\ea7a" } -.codicon-file:before { content: "\ea7b" } -.codicon-file-text:before { content: "\ea7b" } -.codicon-more:before { content: "\ea7c" } -.codicon-ellipsis:before { content: "\ea7c" } -.codicon-kebab-horizontal:before { content: "\ea7c" } -.codicon-mail-reply:before { content: "\ea7d" } -.codicon-reply:before { content: "\ea7d" } -.codicon-organization:before { content: "\ea7e" } -.codicon-organization-filled:before { content: "\ea7e" } -.codicon-organization-outline:before { content: "\ea7e" } -.codicon-new-file:before { content: "\ea7f" } -.codicon-file-add:before { content: "\ea7f" } -.codicon-new-folder:before { content: "\ea80" } -.codicon-file-directory-create:before { content: "\ea80" } -.codicon-trash:before { content: "\ea81" } -.codicon-trashcan:before { content: "\ea81" } -.codicon-history:before { content: "\ea82" } -.codicon-clock:before { content: "\ea82" } -.codicon-folder:before { content: "\ea83" } -.codicon-file-directory:before { content: "\ea83" } -.codicon-symbol-folder:before { content: "\ea83" } -.codicon-logo-github:before { content: "\ea84" } -.codicon-mark-github:before { content: "\ea84" } -.codicon-github:before { content: "\ea84" } -.codicon-terminal:before { content: "\ea85" } -.codicon-console:before { content: "\ea85" } -.codicon-repl:before { content: "\ea85" } -.codicon-zap:before { content: "\ea86" } -.codicon-symbol-event:before { content: "\ea86" } -.codicon-error:before { content: "\ea87" } -.codicon-stop:before { content: "\ea87" } -.codicon-variable:before { content: "\ea88" } -.codicon-symbol-variable:before { content: "\ea88" } -.codicon-array:before { content: "\ea8a" } -.codicon-symbol-array:before { content: "\ea8a" } -.codicon-symbol-module:before { content: "\ea8b" } -.codicon-symbol-package:before { content: "\ea8b" } -.codicon-symbol-namespace:before { content: "\ea8b" } -.codicon-symbol-object:before { content: "\ea8b" } -.codicon-symbol-method:before { content: "\ea8c" } -.codicon-symbol-function:before { content: "\ea8c" } -.codicon-symbol-constructor:before { content: "\ea8c" } -.codicon-symbol-boolean:before { content: "\ea8f" } -.codicon-symbol-null:before { content: "\ea8f" } -.codicon-symbol-numeric:before { content: "\ea90" } -.codicon-symbol-number:before { content: "\ea90" } -.codicon-symbol-structure:before { content: "\ea91" } -.codicon-symbol-struct:before { content: "\ea91" } -.codicon-symbol-parameter:before { content: "\ea92" } -.codicon-symbol-type-parameter:before { content: "\ea92" } -.codicon-symbol-key:before { content: "\ea93" } -.codicon-symbol-text:before { content: "\ea93" } -.codicon-symbol-reference:before { content: "\ea94" } -.codicon-go-to-file:before { content: "\ea94" } -.codicon-symbol-enum:before { content: "\ea95" } -.codicon-symbol-value:before { content: "\ea95" } -.codicon-symbol-ruler:before { content: "\ea96" } -.codicon-symbol-unit:before { content: "\ea96" } -.codicon-activate-breakpoints:before { content: "\ea97" } -.codicon-archive:before { content: "\ea98" } -.codicon-arrow-both:before { content: "\ea99" } -.codicon-arrow-down:before { content: "\ea9a" } -.codicon-arrow-left:before { content: "\ea9b" } -.codicon-arrow-right:before { content: "\ea9c" } -.codicon-arrow-small-down:before { content: "\ea9d" } -.codicon-arrow-small-left:before { content: "\ea9e" } -.codicon-arrow-small-right:before { content: "\ea9f" } -.codicon-arrow-small-up:before { content: "\eaa0" } -.codicon-arrow-up:before { content: "\eaa1" } -.codicon-bell:before { content: "\eaa2" } -.codicon-bold:before { content: "\eaa3" } -.codicon-book:before { content: "\eaa4" } -.codicon-bookmark:before { content: "\eaa5" } -.codicon-debug-breakpoint-conditional-unverified:before { content: "\eaa6" } -.codicon-debug-breakpoint-conditional:before { content: "\eaa7" } -.codicon-debug-breakpoint-conditional-disabled:before { content: "\eaa7" } -.codicon-debug-breakpoint-data-unverified:before { content: "\eaa8" } -.codicon-debug-breakpoint-data:before { content: "\eaa9" } -.codicon-debug-breakpoint-data-disabled:before { content: "\eaa9" } -.codicon-debug-breakpoint-log-unverified:before { content: "\eaaa" } -.codicon-debug-breakpoint-log:before { content: "\eaab" } -.codicon-debug-breakpoint-log-disabled:before { content: "\eaab" } -.codicon-briefcase:before { content: "\eaac" } -.codicon-broadcast:before { content: "\eaad" } -.codicon-browser:before { content: "\eaae" } -.codicon-bug:before { content: "\eaaf" } -.codicon-calendar:before { content: "\eab0" } -.codicon-case-sensitive:before { content: "\eab1" } -.codicon-check:before { content: "\eab2" } -.codicon-checklist:before { content: "\eab3" } -.codicon-chevron-down:before { content: "\eab4" } -.codicon-chevron-left:before { content: "\eab5" } -.codicon-chevron-right:before { content: "\eab6" } -.codicon-chevron-up:before { content: "\eab7" } -.codicon-chrome-close:before { content: "\eab8" } -.codicon-chrome-maximize:before { content: "\eab9" } -.codicon-chrome-minimize:before { content: "\eaba" } -.codicon-chrome-restore:before { content: "\eabb" } -.codicon-circle-outline:before { content: "\eabc" } -.codicon-debug-breakpoint-unverified:before { content: "\eabc" } -.codicon-circle-slash:before { content: "\eabd" } -.codicon-circuit-board:before { content: "\eabe" } -.codicon-clear-all:before { content: "\eabf" } -.codicon-clippy:before { content: "\eac0" } -.codicon-close-all:before { content: "\eac1" } -.codicon-cloud-download:before { content: "\eac2" } -.codicon-cloud-upload:before { content: "\eac3" } -.codicon-code:before { content: "\eac4" } -.codicon-collapse-all:before { content: "\eac5" } -.codicon-color-mode:before { content: "\eac6" } -.codicon-comment-discussion:before { content: "\eac7" } -.codicon-compare-changes:before { content: "\eac8" } -.codicon-credit-card:before { content: "\eac9" } -.codicon-dash:before { content: "\eacc" } -.codicon-dashboard:before { content: "\eacd" } -.codicon-database:before { content: "\eace" } -.codicon-debug-continue:before { content: "\eacf" } -.codicon-debug-disconnect:before { content: "\ead0" } -.codicon-debug-pause:before { content: "\ead1" } -.codicon-debug-restart:before { content: "\ead2" } -.codicon-debug-start:before { content: "\ead3" } -.codicon-debug-step-into:before { content: "\ead4" } -.codicon-debug-step-out:before { content: "\ead5" } -.codicon-debug-step-over:before { content: "\ead6" } -.codicon-debug-stop:before { content: "\ead7" } -.codicon-debug:before { content: "\ead8" } -.codicon-device-camera-video:before { content: "\ead9" } -.codicon-device-camera:before { content: "\eada" } -.codicon-device-mobile:before { content: "\eadb" } -.codicon-diff-added:before { content: "\eadc" } -.codicon-diff-ignored:before { content: "\eadd" } -.codicon-diff-modified:before { content: "\eade" } -.codicon-diff-removed:before { content: "\eadf" } -.codicon-diff-renamed:before { content: "\eae0" } -.codicon-diff:before { content: "\eae1" } -.codicon-discard:before { content: "\eae2" } -.codicon-editor-layout:before { content: "\eae3" } -.codicon-empty-window:before { content: "\eae4" } -.codicon-exclude:before { content: "\eae5" } -.codicon-extensions:before { content: "\eae6" } -.codicon-eye-closed:before { content: "\eae7" } -.codicon-file-binary:before { content: "\eae8" } -.codicon-file-code:before { content: "\eae9" } -.codicon-file-media:before { content: "\eaea" } -.codicon-file-pdf:before { content: "\eaeb" } -.codicon-file-submodule:before { content: "\eaec" } -.codicon-file-symlink-directory:before { content: "\eaed" } -.codicon-file-symlink-file:before { content: "\eaee" } -.codicon-file-zip:before { content: "\eaef" } -.codicon-files:before { content: "\eaf0" } -.codicon-filter:before { content: "\eaf1" } -.codicon-flame:before { content: "\eaf2" } -.codicon-fold-down:before { content: "\eaf3" } -.codicon-fold-up:before { content: "\eaf4" } -.codicon-fold:before { content: "\eaf5" } -.codicon-folder-active:before { content: "\eaf6" } -.codicon-folder-opened:before { content: "\eaf7" } -.codicon-gear:before { content: "\eaf8" } -.codicon-gift:before { content: "\eaf9" } -.codicon-gist-secret:before { content: "\eafa" } -.codicon-gist:before { content: "\eafb" } -.codicon-git-commit:before { content: "\eafc" } -.codicon-git-compare:before { content: "\eafd" } -.codicon-git-merge:before { content: "\eafe" } -.codicon-github-action:before { content: "\eaff" } -.codicon-github-alt:before { content: "\eb00" } -.codicon-globe:before { content: "\eb01" } -.codicon-grabber:before { content: "\eb02" } -.codicon-graph:before { content: "\eb03" } -.codicon-gripper:before { content: "\eb04" } -.codicon-heart:before { content: "\eb05" } -.codicon-home:before { content: "\eb06" } -.codicon-horizontal-rule:before { content: "\eb07" } -.codicon-hubot:before { content: "\eb08" } -.codicon-inbox:before { content: "\eb09" } -.codicon-issue-closed:before { content: "\eb0a" } -.codicon-issue-reopened:before { content: "\eb0b" } -.codicon-issues:before { content: "\eb0c" } -.codicon-italic:before { content: "\eb0d" } -.codicon-jersey:before { content: "\eb0e" } -.codicon-json:before { content: "\eb0f" } -.codicon-kebab-vertical:before { content: "\eb10" } -.codicon-key:before { content: "\eb11" } -.codicon-law:before { content: "\eb12" } -.codicon-lightbulb-autofix:before { content: "\eb13" } -.codicon-link-external:before { content: "\eb14" } -.codicon-link:before { content: "\eb15" } -.codicon-list-ordered:before { content: "\eb16" } -.codicon-list-unordered:before { content: "\eb17" } -.codicon-live-share:before { content: "\eb18" } -.codicon-loading:before { content: "\eb19" } -.codicon-location:before { content: "\eb1a" } -.codicon-mail-read:before { content: "\eb1b" } -.codicon-mail:before { content: "\eb1c" } -.codicon-markdown:before { content: "\eb1d" } -.codicon-megaphone:before { content: "\eb1e" } -.codicon-mention:before { content: "\eb1f" } -.codicon-milestone:before { content: "\eb20" } -.codicon-mortar-board:before { content: "\eb21" } -.codicon-move:before { content: "\eb22" } -.codicon-multiple-windows:before { content: "\eb23" } -.codicon-mute:before { content: "\eb24" } -.codicon-no-newline:before { content: "\eb25" } -.codicon-note:before { content: "\eb26" } -.codicon-octoface:before { content: "\eb27" } -.codicon-open-preview:before { content: "\eb28" } -.codicon-package:before { content: "\eb29" } -.codicon-paintcan:before { content: "\eb2a" } -.codicon-pin:before { content: "\eb2b" } -.codicon-play:before { content: "\eb2c" } -.codicon-run:before { content: "\eb2c" } -.codicon-plug:before { content: "\eb2d" } -.codicon-preserve-case:before { content: "\eb2e" } -.codicon-preview:before { content: "\eb2f" } -.codicon-project:before { content: "\eb30" } -.codicon-pulse:before { content: "\eb31" } -.codicon-question:before { content: "\eb32" } -.codicon-quote:before { content: "\eb33" } -.codicon-radio-tower:before { content: "\eb34" } -.codicon-reactions:before { content: "\eb35" } -.codicon-references:before { content: "\eb36" } -.codicon-refresh:before { content: "\eb37" } -.codicon-regex:before { content: "\eb38" } -.codicon-remote-explorer:before { content: "\eb39" } -.codicon-remote:before { content: "\eb3a" } -.codicon-remove:before { content: "\eb3b" } -.codicon-replace-all:before { content: "\eb3c" } -.codicon-replace:before { content: "\eb3d" } -.codicon-repo-clone:before { content: "\eb3e" } -.codicon-repo-force-push:before { content: "\eb3f" } -.codicon-repo-pull:before { content: "\eb40" } -.codicon-repo-push:before { content: "\eb41" } -.codicon-report:before { content: "\eb42" } -.codicon-request-changes:before { content: "\eb43" } -.codicon-rocket:before { content: "\eb44" } -.codicon-root-folder-opened:before { content: "\eb45" } -.codicon-root-folder:before { content: "\eb46" } -.codicon-rss:before { content: "\eb47" } -.codicon-ruby:before { content: "\eb48" } -.codicon-save-all:before { content: "\eb49" } -.codicon-save-as:before { content: "\eb4a" } -.codicon-save:before { content: "\eb4b" } -.codicon-screen-full:before { content: "\eb4c" } -.codicon-screen-normal:before { content: "\eb4d" } -.codicon-search-stop:before { content: "\eb4e" } -.codicon-server:before { content: "\eb50" } -.codicon-settings-gear:before { content: "\eb51" } -.codicon-settings:before { content: "\eb52" } -.codicon-shield:before { content: "\eb53" } -.codicon-smiley:before { content: "\eb54" } -.codicon-sort-precedence:before { content: "\eb55" } -.codicon-split-horizontal:before { content: "\eb56" } -.codicon-split-vertical:before { content: "\eb57" } -.codicon-squirrel:before { content: "\eb58" } -.codicon-star-full:before { content: "\eb59" } -.codicon-star-half:before { content: "\eb5a" } -.codicon-symbol-class:before { content: "\eb5b" } -.codicon-symbol-color:before { content: "\eb5c" } -.codicon-symbol-constant:before { content: "\eb5d" } -.codicon-symbol-enum-member:before { content: "\eb5e" } -.codicon-symbol-field:before { content: "\eb5f" } -.codicon-symbol-file:before { content: "\eb60" } -.codicon-symbol-interface:before { content: "\eb61" } -.codicon-symbol-keyword:before { content: "\eb62" } -.codicon-symbol-misc:before { content: "\eb63" } -.codicon-symbol-operator:before { content: "\eb64" } -.codicon-symbol-property:before { content: "\eb65" } -.codicon-wrench:before { content: "\eb65" } -.codicon-wrench-subaction:before { content: "\eb65" } -.codicon-symbol-snippet:before { content: "\eb66" } -.codicon-tasklist:before { content: "\eb67" } -.codicon-telescope:before { content: "\eb68" } -.codicon-text-size:before { content: "\eb69" } -.codicon-three-bars:before { content: "\eb6a" } -.codicon-thumbsdown:before { content: "\eb6b" } -.codicon-thumbsup:before { content: "\eb6c" } -.codicon-tools:before { content: "\eb6d" } -.codicon-triangle-down:before { content: "\eb6e" } -.codicon-triangle-left:before { content: "\eb6f" } -.codicon-triangle-right:before { content: "\eb70" } -.codicon-triangle-up:before { content: "\eb71" } -.codicon-twitter:before { content: "\eb72" } -.codicon-unfold:before { content: "\eb73" } -.codicon-unlock:before { content: "\eb74" } -.codicon-unmute:before { content: "\eb75" } -.codicon-unverified:before { content: "\eb76" } -.codicon-verified:before { content: "\eb77" } -.codicon-versions:before { content: "\eb78" } -.codicon-vm-active:before { content: "\eb79" } -.codicon-vm-outline:before { content: "\eb7a" } -.codicon-vm-running:before { content: "\eb7b" } -.codicon-watch:before { content: "\eb7c" } -.codicon-whitespace:before { content: "\eb7d" } -.codicon-whole-word:before { content: "\eb7e" } -.codicon-window:before { content: "\eb7f" } -.codicon-word-wrap:before { content: "\eb80" } -.codicon-zoom-in:before { content: "\eb81" } -.codicon-zoom-out:before { content: "\eb82" } -.codicon-list-filter:before { content: "\eb83" } -.codicon-list-flat:before { content: "\eb84" } -.codicon-list-selection:before { content: "\eb85" } -.codicon-selection:before { content: "\eb85" } -.codicon-list-tree:before { content: "\eb86" } -.codicon-debug-breakpoint-function-unverified:before { content: "\eb87" } -.codicon-debug-breakpoint-function:before { content: "\eb88" } -.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" } -.codicon-debug-stackframe-active:before { content: "\eb89" } -.codicon-debug-stackframe-dot:before { content: "\eb8a" } -.codicon-debug-stackframe:before { content: "\eb8b" } -.codicon-debug-stackframe-focused:before { content: "\eb8b" } -.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } -.codicon-symbol-string:before { content: "\eb8d" } -.codicon-debug-reverse-continue:before { content: "\eb8e" } -.codicon-debug-step-back:before { content: "\eb8f" } -.codicon-debug-restart-frame:before { content: "\eb90" } -.codicon-debug-alternate:before { content: "\eb91" } -.codicon-call-incoming:before { content: "\eb92" } -.codicon-call-outgoing:before { content: "\eb93" } -.codicon-menu:before { content: "\eb94" } -.codicon-expand-all:before { content: "\eb95" } -.codicon-feedback:before { content: "\eb96" } -.codicon-group-by-ref-type:before { content: "\eb97" } -.codicon-ungroup-by-ref-type:before { content: "\eb98" } -.codicon-account:before { content: "\eb99" } -.codicon-bell-dot:before { content: "\eb9a" } -.codicon-debug-console:before { content: "\eb9b" } -.codicon-library:before { content: "\eb9c" } -.codicon-output:before { content: "\eb9d" } -.codicon-run-all:before { content: "\eb9e" } -.codicon-sync-ignored:before { content: "\eb9f" } -.codicon-debug-alt-2:before { content: "\f101" } -.codicon-debug-alt:before { content: "\f102" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css b/src/vs/base/browser/ui/codicons/codicon/codicon-animations.css similarity index 100% rename from src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css rename to src/vs/base/browser/ui/codicons/codicon/codicon-animations.css diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-modifications.css b/src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css similarity index 100% rename from src/vs/base/browser/ui/codiconLabel/codicon/codicon-modifications.css rename to src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.css b/src/vs/base/browser/ui/codicons/codicon/codicon.css new file mode 100644 index 0000000000..0bdf858723 --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codicon/codicon.css @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +@font-face { + font-family: "codicon"; + src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype"); +} + +.codicon[class*='codicon-'] { + font: normal normal normal 16px/1 codicon; + display: inline-block; + text-decoration: none; + text-rendering: auto; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + user-select: none; + -webkit-user-select: none; + -ms-user-select: none; +} + +/* icon rules are dynamically created in codiconStyles */ diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf similarity index 89% rename from src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf rename to src/vs/base/browser/ui/codicons/codicon/codicon.ttf index d4358ace62..ab67bd300f 100644 Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codicons/codiconLabel.ts similarity index 84% rename from src/vs/base/browser/ui/codiconLabel/codiconLabel.ts rename to src/vs/base/browser/ui/codicons/codiconLabel.ts index fbf05404a2..d851fe33e3 100644 --- a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts +++ b/src/vs/base/browser/ui/codicons/codiconLabel.ts @@ -3,9 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./codicon/codicon'; -import 'vs/css!./codicon/codicon-modifications'; -import 'vs/css!./codicon/codicon-animations'; import { escape } from 'vs/base/common/strings'; import { renderCodicons } from 'vs/base/common/codicons'; diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts new file mode 100644 index 0000000000..1fdeaa6f07 --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./codicon/codicon'; +import 'vs/css!./codicon/codicon-modifications'; +import 'vs/css!./codicon/codicon-animations'; + +import { Codicon, iconRegistry } from 'vs/base/common/codicons'; +import { createStyleSheet } from 'vs/base/browser/dom'; +import { RunOnceScheduler } from 'vs/base/common/async'; + +function initialize() { + let codiconStyleSheet = createStyleSheet(); + codiconStyleSheet.id = 'codiconStyles'; + + function updateAll() { + const rules = []; + for (let c of iconRegistry.all) { + rules.push(formatRule(c)); + } + codiconStyleSheet.innerHTML = rules.join('\n'); + } + + const delayer = new RunOnceScheduler(updateAll, 0); + iconRegistry.onDidRegister(() => delayer.schedule()); + delayer.schedule(); +} + +function formatRule(c: Codicon) { + let def = c.definition; + while (def instanceof Codicon) { + def = def.definition; + } + return `.codicon-${c.id}:before { content: '${def.character}'; }`; +} + +initialize(); diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index b3513af538..0a33b9a273 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -6,7 +6,7 @@ import 'vs/css!./dialog'; import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; -import { $, hide, show, EventHelper, clearNode, removeClasses, addClass, addClasses, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { $, hide, show, EventHelper, clearNode, removeClasses, addClasses, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -17,6 +17,7 @@ import { Action } from 'vs/base/common/actions'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { SimpleCheckbox, ISimpleCheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; export interface IDialogOptions { cancelId?: number; @@ -37,6 +38,9 @@ export interface IDialogStyles extends IButtonStyles, ISimpleCheckboxStyles { dialogBackground?: Color; dialogShadow?: Color; dialogBorder?: Color; + errorIconForeground?: Color; + warningIconForeground?: Color; + infoIconForeground?: Color; } interface ButtonMapEntry { @@ -44,6 +48,11 @@ interface ButtonMapEntry { index: number; } +const dialogErrorIcon = registerIcon('dialog-error', Codicon.error); +const dialogWarningIcon = registerIcon('dialog-warning', Codicon.warning); +const dialogInfoIcon = registerIcon('dialog-info', Codicon.info); +const dialogCloseIcon = registerIcon('dialog-close', Codicon.close); + export class Dialog extends Disposable { private element: HTMLElement | undefined; private shadowElement: HTMLElement | undefined; @@ -202,30 +211,29 @@ export class Dialog extends Disposable { } })); - addClass(this.iconElement, 'codicon'); - removeClasses(this.iconElement, 'codicon-alert', 'codicon-warning', 'codicon-info'); + removeClasses(this.iconElement, dialogErrorIcon.classNames, dialogWarningIcon.classNames, dialogInfoIcon.classNames, Codicon.loading.classNames); switch (this.options.type) { case 'error': - addClass(this.iconElement, 'codicon-error'); + addClasses(this.iconElement, dialogErrorIcon.classNames); break; case 'warning': - addClass(this.iconElement, 'codicon-warning'); + addClasses(this.iconElement, dialogWarningIcon.classNames); break; case 'pending': - addClasses(this.iconElement, 'codicon-loading', 'codicon-animation-spin'); + addClasses(this.iconElement, Codicon.loading.classNames, 'codicon-animation-spin'); break; case 'none': case 'info': case 'question': default: - addClass(this.iconElement, 'codicon-info'); + addClasses(this.iconElement, dialogInfoIcon.classNames); break; } const actionBar = new ActionBar(this.toolbarContainer, {}); - const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), 'codicon codicon-close', true, () => { + const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, () => { resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); return Promise.resolve(); }); @@ -268,10 +276,28 @@ export class Dialog extends Disposable { this.checkbox.style(style); } - if (this.messageDetailElement) { + if (this.messageDetailElement && fgColor && bgColor) { const messageDetailColor = Color.fromHex(fgColor).transparent(.9); this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); } + + if (this.iconElement) { + let color; + switch (this.options.type) { + case 'error': + color = style.errorIconForeground; + break; + case 'warning': + color = style.warningIconForeground; + break; + default: + color = style.infoIconForeground; + break; + } + if (color) { + this.iconElement.style.color = color.toString(); + } + } } } diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 0cd0bd8326..6c87e8734e 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -295,7 +295,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { render(container: HTMLElement): void { const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { - this.element = append(el, $('a.action-label.codicon')); + this.element = append(el, $('a.action-label.codicon')); // todo@aeschli: remove codicon, should come through `this.clazz` if (this.clazz) { addClasses(this.element, this.clazz); } diff --git a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts index 5d8a17b036..0f34a7f0a5 100644 --- a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts +++ b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts @@ -6,6 +6,7 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { Color } from 'vs/base/common/color'; import * as nls from 'vs/nls'; +import { Codicon } from 'vs/base/common/codicons'; export interface IFindInputCheckboxOpts { readonly appendTitle: string; @@ -21,7 +22,7 @@ const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular E export class CaseSensitiveCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-case-sensitive', + icon: Codicon.caseSensitive, title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, @@ -33,7 +34,7 @@ export class CaseSensitiveCheckbox extends Checkbox { export class WholeWordsCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-whole-word', + icon: Codicon.wholeWord, title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, @@ -45,7 +46,7 @@ export class WholeWordsCheckbox extends Checkbox { export class RegexCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ - actionClassName: 'codicon-regex', + icon: Codicon.regex, title: NLS_REGEX_CHECKBOX_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 20925b41b8..bfbbe3b02a 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -17,6 +17,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { ICheckboxStyles, Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IFindInputCheckboxOpts } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; +import { Codicon } from 'vs/base/common/codicons'; export interface IReplaceInputOptions extends IReplaceInputStyles { readonly placeholder?: string; @@ -42,7 +43,7 @@ export class PreserveCaseCheckbox extends Checkbox { constructor(opts: IFindInputCheckboxOpts) { super({ // TODO: does this need its own icon? - actionClassName: 'codicon-preserve-case', + icon: Codicon.preserveCase, title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle, isChecked: opts.isChecked, inputActiveOptionBorder: opts.inputActiveOptionBorder, diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index ac9e8e3a3c..f30baff33a 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -594,7 +594,7 @@ export class InputBox extends Widget { this.element.style.backgroundColor = background; this.element.style.color = foreground; - this.input.style.backgroundColor = background; + this.input.style.backgroundColor = 'inherit'; this.input.style.color = foreground; this.element.style.borderWidth = border ? '1px' : ''; diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 6e8e13f9e0..446d52293a 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -129,10 +129,6 @@ cursor: pointer; } -.monaco-list-type-filter > .controls > .filter:checked::before { - content: "\eb83" !important; /* codicon-list-filter */ -} - .monaco-list-type-filter > .controls > .filter { margin-left: 4px; } diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index d20bf85bdf..4e41645e5f 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -63,17 +63,6 @@ export interface IIdentityProvider { getId(element: T): { toString(): string; }; } -export enum ListAriaRootRole { - /** default list structure role */ - LIST = 'list', - - /** default tree structure role */ - TREE = 'tree', - - /** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */ - FORM = 'form' -} - export interface IKeyboardNavigationLabelProvider { /** diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 850b482571..bc00bfac4f 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -7,10 +7,11 @@ import 'vs/css!./list'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { range } from 'vs/base/common/arrays'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent } from './list'; -import { List, IListStyles, IListOptions } from './listWidget'; +import { List, IListStyles, IListOptions, IListAccessibilityProvider } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; import { Event } from 'vs/base/common/event'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; export interface IPagedRenderer extends IListRenderer { renderPlaceholder(index: number, templateData: TTemplateData): void; @@ -70,6 +71,54 @@ class PagedRenderer implements IListRenderer implements IListAccessibilityProvider { + + constructor( + private modelProvider: () => IPagedModel, + private accessibilityProvider: IListAccessibilityProvider + ) { } + + getWidgetAriaLabel(): string { + return this.accessibilityProvider.getWidgetAriaLabel(); + } + + getAriaLabel(index: number): string | null { + const model = this.modelProvider(); + + if (!model.isResolved(index)) { + return null; + } + + return this.accessibilityProvider.getAriaLabel(model.get(index)); + } +} + +export interface IPagedListOptions { + readonly enableKeyboardNavigation?: boolean; + readonly automaticKeyboardNavigation?: boolean; + readonly ariaLabel?: string; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly accessibilityProvider?: IListAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly setRowHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; + readonly additionalScrollHeight?: number; +} + +function fromPagedListOptions(modelProvider: () => IPagedModel, options: IPagedListOptions): IListOptions { + return { + ...options, + accessibilityProvider: options.accessibilityProvider && new PagedAccessibilityProvider(modelProvider, options.accessibilityProvider) + }; +} + export class PagedList implements IDisposable { private list: List; @@ -80,10 +129,11 @@ export class PagedList implements IDisposable { container: HTMLElement, virtualDelegate: IListVirtualDelegate, renderers: IPagedRenderer[], - options: IListOptions = {} + options: IPagedListOptions = {} ) { - const pagedRenderers = renderers.map(r => new PagedRenderer>(r, () => this.model)); - this.list = new List(user, container, virtualDelegate, pagedRenderers, options); + const modelProvider = () => this.model; + const pagedRenderers = renderers.map(r => new PagedRenderer>(r, modelProvider)); + this.list = new List(user, container, virtualDelegate, pagedRenderers, fromPagedListOptions(modelProvider, options)); } getHTMLElement(): HTMLElement { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 49dfcd9620..3d49dc3b2b 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -41,11 +41,11 @@ export interface IListViewDragAndDrop extends IListDragAndDrop { getDragElements(element: T): T[]; } -export interface IAriaProvider { - getSetSize(element: T, index: number, listLength: number): number; - getPosInSet(element: T, index: number): number; +export interface IListViewAccessibilityProvider { + getSetSize?(element: T, index: number, listLength: number): number; + getPosInSet?(element: T, index: number): number; getRole?(element: T): string; - isChecked?(element: T): boolean; + isChecked?(element: T): boolean | undefined; } export interface IListViewOptions { @@ -57,7 +57,7 @@ export interface IListViewOptions { readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; - readonly ariaProvider?: IAriaProvider; + readonly accessibilityProvider?: IListViewAccessibilityProvider; readonly additionalScrollHeight?: number; } @@ -152,6 +152,40 @@ function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): return f1 === f2; } +class ListViewAccessibilityProvider implements Required> { + + readonly getSetSize: (element: any, index: number, listLength: number) => number; + readonly getPosInSet: (element: any, index: number) => number; + readonly getRole: (element: T) => string; + readonly isChecked: (element: T) => boolean | undefined; + + constructor(accessibilityProvider?: IListViewAccessibilityProvider) { + if (accessibilityProvider?.getSetSize) { + this.getSetSize = accessibilityProvider.getSetSize.bind(accessibilityProvider); + } else { + this.getSetSize = (e, i, l) => l; + } + + if (accessibilityProvider?.getPosInSet) { + this.getPosInSet = accessibilityProvider.getPosInSet.bind(accessibilityProvider); + } else { + this.getPosInSet = (e, i) => i + 1; + } + + if (accessibilityProvider?.getRole) { + this.getRole = accessibilityProvider.getRole.bind(accessibilityProvider); + } else { + this.getRole = _ => 'listitem'; + } + + if (accessibilityProvider?.isChecked) { + this.isChecked = accessibilityProvider.isChecked.bind(accessibilityProvider); + } else { + this.isChecked = _ => undefined; + } + } +} + export class ListView implements ISpliceable, IDisposable { private static InstanceCount = 0; @@ -181,7 +215,7 @@ export class ListView implements ISpliceable, IDisposable { private supportDynamicHeights: boolean; private horizontalScrolling: boolean; private additionalScrollHeight: number; - private ariaProvider: IAriaProvider; + private accessibilityProvider: ListViewAccessibilityProvider; private scrollWidth: number | undefined; private dnd: IListViewDragAndDrop; @@ -237,7 +271,7 @@ export class ListView implements ISpliceable, IDisposable { this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; - this.ariaProvider = options.ariaProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 }; + this.accessibilityProvider = new ListViewAccessibilityProvider(options.accessibilityProvider); this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; @@ -611,11 +645,11 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); - const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'listitem'; + const role = this.accessibilityProvider.getRole(item.element); item.row!.domNode!.setAttribute('role', role); - const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined; + const checked = this.accessibilityProvider.isChecked(item.element); if (typeof checked !== 'undefined') { - item.row!.domNode!.setAttribute('aria-checked', String(checked)); + item.row!.domNode!.setAttribute('aria-checked', String(!!checked)); } } @@ -687,8 +721,8 @@ export class ListView implements ISpliceable, IDisposable { item.row!.domNode!.setAttribute('data-index', `${index}`); item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaProvider.getSetSize(item.element, index, this.length))); - item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaProvider.getPosInSet(item.element, index))); + item.row!.domNode!.setAttribute('aria-setsize', String(this.accessibilityProvider.getSetSize(item.element, index, this.length))); + item.row!.domNode!.setAttribute('aria-posinset', String(this.accessibilityProvider.getPosInSet(item.element, index))); item.row!.domNode!.setAttribute('id', this.getElementDomId(index)); DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 6c6b7319f7..d4ea989b6d 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,8 +16,8 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaProvider } from './listView'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -686,25 +686,11 @@ export interface IStyleController { style(styles: IListStyles): void; } -export interface IAccessibilityProvider { - - /** - * Given an element in the tree, return the ARIA label that should be associated with the - * item. This helps screen readers to provide a meaningful label for the currently focused - * tree element. - * - * Returning null will not disable ARIA for the element. Instead it is up to the screen reader - * to compute a meaningful label based on the contents of the element in the DOM - * - * See also: https://www.w3.org/TR/wai-aria/#aria-label - */ +export interface IListAccessibilityProvider extends IListViewAccessibilityProvider { getAriaLabel(element: T): string | null; - - /** - * https://www.w3.org/TR/wai-aria/#aria-level - */ + getWidgetAriaLabel(): string; + getWidgetRole?(): string; getAriaLevel?(element: T): number | undefined; - onDidChangeActiveDescendant?: Event; getActiveDescendantId?(element: T): string | undefined; } @@ -836,14 +822,12 @@ export interface IListOptions { readonly automaticKeyboardNavigation?: boolean; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; - readonly ariaRole?: ListAriaRootRole | string; - readonly ariaLabel?: string; readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; readonly openController?: IOpenController; readonly styleController?: (suffix: string) => IStyleController; - readonly accessibilityProvider?: IAccessibilityProvider; + readonly accessibilityProvider?: IListAccessibilityProvider; // list view options readonly useShadows?: boolean; @@ -853,7 +837,6 @@ export interface IListOptions { readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; - readonly ariaProvider?: IAriaProvider; readonly additionalScrollHeight?: number; } @@ -894,7 +877,7 @@ const defaultStyles: IListStyles = { treeIndentGuidesStroke: Color.fromHex('#a9a9a9') }; -const DefaultOptions = { +const DefaultOptions: IListOptions = { keyboardSupport: true, mouseSupport: true, multipleSelectionSupport: true, @@ -903,8 +886,7 @@ const DefaultOptions = { onDragStart(): void { }, onDragOver() { return false; }, drop() { } - }, - ariaRootRole: ListAriaRootRole.LIST + } }; // TODO@Joao: move these utils into a SortedArray class @@ -1036,7 +1018,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { } + constructor(private accessibilityProvider: IListAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -1123,7 +1105,8 @@ export class List implements ISpliceable, IDisposable { private spliceable: ISpliceable; private styleController: IStyleController; private typeLabelController?: TypeLabelController; - private accessibilityProvider?: IAccessibilityProvider; + private accessibilityProvider?: IListAccessibilityProvider; + private _ariaLabel: string = ''; protected readonly disposables = new DisposableStore(); @@ -1202,7 +1185,8 @@ export class List implements ISpliceable, IDisposable { renderers: IListRenderer[], private _options: IListOptions = DefaultOptions ) { - this.selection = new SelectionTrait(this._options.ariaRole !== 'listbox'); + const role = this._options.accessibilityProvider && this._options.accessibilityProvider.getWidgetRole ? this._options.accessibilityProvider?.getWidgetRole() : 'list'; + this.selection = new SelectionTrait(role !== 'listbox'); this.focus = new Trait('focused'); mixin(_options, defaultStyles, false); @@ -1227,12 +1211,7 @@ export class List implements ISpliceable, IDisposable { }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); - - if (typeof _options.ariaRole !== 'string') { - this.view.domNode.setAttribute('role', ListAriaRootRole.LIST); - } else { - this.view.domNode.setAttribute('role', _options.ariaRole); - } + this.view.domNode.setAttribute('role', role); if (_options.styleController) { this.styleController = _options.styleController(this.view.domId); @@ -1273,8 +1252,8 @@ export class List implements ISpliceable, IDisposable { this.onDidChangeFocus(this._onFocusChange, this, this.disposables); this.onDidChangeSelection(this._onSelectionChange, this, this.disposables); - if (_options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); + if (this.accessibilityProvider) { + this.ariaLabel = this.accessibilityProvider.getWidgetAriaLabel(); } if (_options.multipleSelectionSupport) { this.view.domNode.setAttribute('aria-multiselectable', 'true'); @@ -1377,6 +1356,15 @@ export class List implements ISpliceable, IDisposable { return this.view.lastVisibleIndex; } + get ariaLabel(): string { + return this._ariaLabel; + } + + set ariaLabel(value: string) { + this._ariaLabel = value; + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", value)); + } + domFocus(): void { this.view.domNode.focus(); } diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index d06de59527..b549947f17 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -171,6 +171,7 @@ .menubar.compact { flex-shrink: 0; + overflow: visible; /* to avoid the compact menu to be repositioned when clicking */ } .menubar.compact > .menubar-menu-button { @@ -201,7 +202,7 @@ } .menubar.compact .toolbar-toggle-more { - position: absolute; + position: relative; left: 0px; top: 0px; cursor: pointer; diff --git a/src/vs/base/browser/ui/menu/menu.svg b/src/vs/base/browser/ui/menu/menu.svg deleted file mode 100644 index 1b61c97822..0000000000 --- a/src/vs/base/browser/ui/menu/menu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index db8120018b..ca1f47f80a 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -19,11 +19,14 @@ import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { stripCodicons } from 'vs/base/common/codicons'; +import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons'; export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g; +const menuSelectionIcon = registerIcon('menu-selection', Codicon.check); +const menuSubmenuIcon = registerIcon('menu-submenu', Codicon.chevronRight); + export enum Direction { Right, Left @@ -423,7 +426,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - this.check = append(this.item, $('span.menu-item-check.codicon.codicon-check')); + this.check = append(this.item, $('span.menu-item-check' + menuSelectionIcon.cssSelector)); this.check.setAttribute('role', 'none'); this.label = append(this.item, $('span.action-label')); @@ -670,7 +673,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { addClass(this.item, 'monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); this.updateAriaExpanded('false'); - this.submenuIndicator = append(this.item, $('span.submenu-indicator.codicon.codicon-chevron-right')); + this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); this.submenuIndicator.setAttribute('aria-hidden', 'true'); } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 55171fc9d6..f1554b811a 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -22,9 +22,12 @@ import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; import { isMacintosh } from 'vs/base/common/platform'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; const $ = DOM.$; +const menuBarMoreIcon = registerIcon('menubar-more', Codicon.more); + export interface IMenuBarOptions { enableMnemonics?: boolean; disableAltFocus?: boolean; @@ -313,7 +316,7 @@ export class MenuBar extends Disposable { const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More'); const title = this.options.compactMode !== undefined ? label : undefined; const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); - const titleElement = $('div.menubar-menu-title.toolbar-toggle-more.codicon.codicon-more', { 'role': 'none', 'aria-hidden': true }); + const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + menuBarMoreIcon.cssSelector, { 'role': 'none', 'aria-hidden': true }); buttonElement.appendChild(titleElement); this.container.appendChild(buttonElement); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 25934addfa..120ee44648 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,6 +9,11 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + + +const scrollbarButtonLeftIcon = registerIcon('scrollbar-button-left', Codicon.triangleLeft); +const scrollbarButtonRightIcon = registerIcon('scrollbar-button-right', Codicon.triangleRight); export class HorizontalScrollbar extends AbstractScrollbar { @@ -36,7 +41,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2; this._createArrow({ - className: 'scra codicon codicon-triangle-left', + className: 'scra', + icon: scrollbarButtonLeftIcon, top: scrollbarDelta, left: arrowDelta, bottom: undefined, @@ -47,7 +53,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'scra codicon codicon-triangle-right', + className: 'scra', + icon: scrollbarButtonRightIcon, top: scrollbarDelta, left: undefined, bottom: undefined, diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index f0d3f0d4c8..0e3a67b820 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scrollbars'; -import { isEdge } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -266,7 +265,7 @@ export abstract class AbstractScrollableElement extends Widget { } public setScrollDimensions(dimensions: INewScrollDimensions): void { - this._scrollable.setScrollDimensions(dimensions); + this._scrollable.setScrollDimensions(dimensions, false); } /** @@ -336,7 +335,7 @@ export abstract class AbstractScrollableElement extends Widget { this._onMouseWheel(new StandardWheelEvent(browserEvent)); }; - this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdge ? 'mousewheel' : 'wheel', onMouseWheel, { passive: false })); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); } } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index a5a16e8926..1adbf8aeda 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -7,6 +7,8 @@ import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveM import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Widget } from 'vs/base/browser/ui/widget'; import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; +import { addClasses } from 'vs/base/browser/dom'; /** * The arrow image size. @@ -16,6 +18,7 @@ export const ARROW_IMG_SIZE = 11; export interface ScrollbarArrowOptions { onActivate: () => void; className: string; + icon: Codicon; bgWidth: number; bgHeight: number; @@ -59,6 +62,8 @@ export class ScrollbarArrow extends Widget { this.domNode = document.createElement('div'); this.domNode.className = opts.className; + addClasses(this.domNode, opts.icon.classNames); + this.domNode.style.position = 'absolute'; this.domNode.style.width = ARROW_IMG_SIZE + 'px'; this.domNode.style.height = ARROW_IMG_SIZE + 'px'; diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index a59e60bd50..2d28938ef3 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,6 +9,10 @@ import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/s import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +const scrollbarButtonUpIcon = registerIcon('scrollbar-button-up', Codicon.triangleUp); +const scrollbarButtonDownIcon = registerIcon('scrollbar-button-down', Codicon.triangleDown); export class VerticalScrollbar extends AbstractScrollbar { @@ -37,7 +41,8 @@ export class VerticalScrollbar extends AbstractScrollbar { let scrollbarDelta = (options.verticalScrollbarSize - ARROW_IMG_SIZE) / 2; this._createArrow({ - className: 'scra codicon codicon-triangle-up', + className: 'scra', + icon: scrollbarButtonUpIcon, top: arrowDelta, left: scrollbarDelta, bottom: undefined, @@ -48,7 +53,8 @@ export class VerticalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'scra codicon codicon-triangle-down', + className: 'scra', + icon: scrollbarButtonDownIcon, top: undefined, left: scrollbarDelta, bottom: arrowDelta, diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index ee0537e1a6..d2dc1c966e 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -20,6 +20,7 @@ import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxSty import { isMacintosh } from 'vs/base/common/platform'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -732,12 +733,20 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.listRenderer = new SelectListRenderer(); this.selectList = new List('SelectBoxCustom', this.selectDropDownListContainer, this, [this.listRenderer], { - ariaLabel: this.selectBoxOptions.ariaLabel, useShadows: false, verticalScrollMode: ScrollbarVisibility.Visible, keyboardSupport: false, - mouseSupport: false + mouseSupport: false, + accessibilityProvider: { + getAriaLabel: (element) => element.text, + getWidgetAriaLabel: () => localize('selectBox', "Select Box"), + getRole: () => 'option', + getWidgetRole: () => 'listbox' + } }); + if (this.selectBoxOptions.ariaLabel) { + this.selectList.ariaLabel = this.selectBoxOptions.ariaLabel; + } // SetUp list keyboard controller - control navigation, disabled items, focus const onSelectDropDownKeyDown = Event.chain(domEvent(this.selectDropDownListContainer, 'keydown')) diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index a6818c561d..1d9b1b519b 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -236,6 +236,7 @@ export abstract class Pane extends Disposable implements IView { const height = this._orientation === Orientation.VERTICAL ? size - headerSize : this.orthogonalSize - headerSize; if (this.isExpanded()) { + toggleClass(this.body, 'wide', width >= 600); this.layoutBody(height, width); this.expandedSize = size; } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 05cbba844f..0bb6b36e1d 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -12,9 +12,12 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; export const CONTEXT = 'context.toolbar'; +const toolBarMoreIcon = registerIcon('toolbar-more', Codicon.more); + export interface IToolBarOptions { orientation?: ActionsOrientation; actionViewItemProvider?: IActionViewItemProvider; @@ -65,7 +68,7 @@ export class ToolBar extends Disposable { this.options.actionViewItemProvider, this.actionRunner, this.options.getKeyBinding, - 'codicon-more', + toolBarMoreIcon.classNames, this.options.anchorAlignmentProvider ); this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index f734a7089c..2220e1cb57 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate, ListAriaRootRole } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode, addClasses, removeClasses } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -26,6 +26,7 @@ import { values } from 'vs/base/common/map'; import { clamp } from 'vs/base/common/numbers'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { SetMap } from 'vs/base/common/collections'; +import { treeItemExpandedIcon, treeFilterOnTypeOnIcon, treeFilterOnTypeOffIcon, treeFilterClearIcon } from 'vs/base/browser/ui/tree/treeIcons'; class TreeElementsDragAndDropData extends ElementsDragAndDropData { @@ -162,9 +163,30 @@ function asListOptions(modelProvider: () => ITreeModel { + return options.accessibilityProvider!.isChecked!(node.element); + } : undefined, + getRole: options.accessibilityProvider && options.accessibilityProvider.getRole ? (node) => { + return options.accessibilityProvider!.getRole!(node.element); + } : () => 'treeitem', getAriaLabel(e) { return options.accessibilityProvider!.getAriaLabel(e.element); }, + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider && options.accessibilityProvider.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel(node) { return node.depth; }, @@ -178,27 +200,7 @@ function asListOptions(modelProvider: () => ITreeModel { - return options.ariaProvider!.isChecked!(node.element); - } : undefined, - getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => { - return options.ariaProvider!.getRole!(node.element); - } : () => 'treeitem' - }, - ariaRole: ListAriaRootRole.TREE + enableKeyboardNavigation: options.simpleKeyboardNavigation }; } @@ -404,10 +406,10 @@ class TreeRenderer implements IListRenderer } if (node.collapsible && (!this.hideTwistiesOfChildlessElements || node.visibleChildrenCount > 0)) { - addClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible'); + addClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible'); toggleClass(templateData.twistie, 'collapsed', node.collapsed); } else { - removeClasses(templateData.twistie, 'codicon', 'codicon-chevron-down', 'collapsible', 'collapsed'); + removeClasses(templateData.twistie, treeItemExpandedIcon.classNames, 'collapsible', 'collapsed'); } if (node.collapsible) { @@ -645,14 +647,14 @@ class TypeFilterController implements IDisposable { const controls = append(this.domNode, $('.controls')); this._filterOnType = !!tree.options.filterOnType; - this.filterOnTypeDomNode = append(controls, $('input.filter.codicon.codicon-list-selection')); + this.filterOnTypeDomNode = append(controls, $('input.filter')); this.filterOnTypeDomNode.type = 'checkbox'; this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; - this.updateFilterOnTypeTitle(); + this.updateFilterOnTypeTitleAndIcon(); domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); - this.clearDomNode = append(controls, $('button.clear.codicon.codicon-close')); + this.clearDomNode = append(controls, $('button.clear' + treeFilterClearIcon.cssSelector)); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); @@ -858,13 +860,17 @@ class TypeFilterController implements IDisposable { this.tree.refilter(); this.tree.domFocus(); this.render(); - this.updateFilterOnTypeTitle(); + this.updateFilterOnTypeTitleAndIcon(); } - private updateFilterOnTypeTitle(): void { + private updateFilterOnTypeTitleAndIcon(): void { if (this.filterOnType) { + removeClasses(this.filterOnTypeDomNode, treeFilterOnTypeOffIcon.classNames); + addClasses(this.filterOnTypeDomNode, treeFilterOnTypeOnIcon.classNames); this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); } else { + removeClasses(this.filterOnTypeDomNode, treeFilterOnTypeOnIcon.classNames); + addClasses(this.filterOnTypeDomNode, treeFilterOnTypeOffIcon.classNames); this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); } } @@ -1445,6 +1451,14 @@ export abstract class AbstractTree implements IDisposable return node.element; } + get ariaLabel(): string { + return this.view.ariaLabel; + } + + set ariaLabel(value: string) { + this.view.ariaLabel = value; + } + domFocus(): void { this.view.domFocus(); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index d8dad4a731..a9a7328548 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -5,7 +5,7 @@ import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; -import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; @@ -15,12 +15,13 @@ import { Iterable } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; -import { toggleClass } from 'vs/base/browser/dom'; +import { removeClasses, addClasses } from 'vs/base/browser/dom'; import { values } from 'vs/base/common/map'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { IThemable } from 'vs/base/common/styler'; import { isFilterResult, getVisibleState } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { treeItemLoadingIcon } from 'vs/base/browser/ui/tree/treeIcons'; interface IAsyncDataTreeNode { element: TInput | T; @@ -109,7 +110,11 @@ class AsyncDataTreeRenderer implements IT } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'codicon-loading', element.slow); + if (element.slow) { + addClasses(twistieElement, treeItemLoadingIcon.classNames); + } else { + removeClasses(twistieElement, treeItemLoadingIcon.classNames); + } return false; } @@ -231,9 +236,21 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt }, accessibilityProvider: options.accessibilityProvider && { ...options.accessibilityProvider, + getPosInSet: undefined, + getSetSize: undefined, + getRole: options.accessibilityProvider!.getRole ? (el) => { + return options.accessibilityProvider!.getRole!(el.element as T); + } : () => 'treeitem', + isChecked: options.accessibilityProvider!.isChecked ? (e) => { + return !!(options.accessibilityProvider?.isChecked!(e.element as T)); + } : undefined, getAriaLabel(e) { return options.accessibilityProvider!.getAriaLabel(e.element as T); }, + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider!.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel: options.accessibilityProvider!.getAriaLevel && (node => { return options.accessibilityProvider!.getAriaLevel!(node.element as T); }), @@ -258,21 +275,6 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaProvider: options.ariaProvider && { - getPosInSet(el, index) { - return options.ariaProvider!.getPosInSet(el.element as T, index); - }, - getSetSize(el, index, listLength) { - return options.ariaProvider!.getSetSize(el.element as T, index, listLength); - }, - getRole: options.ariaProvider!.getRole ? (el) => { - return options.ariaProvider!.getRole!(el.element as T); - } : () => 'treeitem', - isChecked: options.ariaProvider!.isChecked ? (e) => { - return !!(options.ariaProvider?.isChecked!(e.element as T)); - } : undefined - }, - ariaRole: ListAriaRootRole.TREE, additionalScrollHeight: options.additionalScrollHeight }; } @@ -448,6 +450,14 @@ export class AsyncDataTree implements IDisposable return this.tree.lastVisibleElement!.element as T; } + get ariaLabel(): string { + return this.tree.ariaLabel; + } + + set ariaLabel(value: string) { + this.tree.ariaLabel = value; + } + domFocus(): void { this.tree.domFocus(); } @@ -1044,7 +1054,11 @@ class CompressibleAsyncDataTreeRenderer i } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'codicon-loading', element.slow); + if (element.slow) { + addClasses(twistieElement, treeItemLoadingIcon.classNames); + } else { + removeClasses(twistieElement, treeItemLoadingIcon.classNames); + } return false; } diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 61cb32a82c..cd5e1baad2 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -60,6 +60,6 @@ transform: rotate(-90deg); } -.monaco-tl-twistie.codicon-loading::before { +.monaco-tl-twistie.codicon-tree-item-loading::before { animation: codicon-spin 1.25s linear infinite; } diff --git a/src/vs/base/browser/ui/tree/treeIcons.ts b/src/vs/base/browser/ui/tree/treeIcons.ts new file mode 100644 index 0000000000..668c523294 --- /dev/null +++ b/src/vs/base/browser/ui/tree/treeIcons.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon, registerIcon } from 'vs/base/common/codicons'; + +export const treeItemExpandedIcon = registerIcon('tree-item-expanded', Codicon.chevronDown); // collapsed is done with rotation + +export const treeFilterOnTypeOnIcon = registerIcon('tree-filter-on-type-on', Codicon.listFilter); +export const treeFilterOnTypeOffIcon = registerIcon('tree-filter-on-type-off', Codicon.listSelection); +export const treeFilterClearIcon = registerIcon('tree-filter-clear', Codicon.close); + +export const treeItemLoadingIcon = registerIcon('tree-item-loading', Codicon.loading); diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index b7c5b4e594..95a64ade15 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -4,6 +4,473 @@ *--------------------------------------------------------------------------------------------*/ import { codiconStartMarker } from 'vs/base/common/codicon'; +import { Emitter, Event } from 'vs/base/common/event'; + +export interface IIconRegistry { + readonly all: IterableIterator; + readonly onDidRegister: Event; + get(id: string): Codicon | undefined; +} + +class Registry implements IIconRegistry { + + private readonly _icons = new Map(); + private readonly _onDidRegister = new Emitter(); + + public add(icon: Codicon) { + if (!this._icons.has(icon.id)) { + this._icons.set(icon.id, icon); + this._onDidRegister.fire(icon); + } else { + console.error(`Duplicate registration of codicon ${icon.id}`); + } + } + + public get(id: string): Codicon | undefined { + return this._icons.get(id); + } + + public get all(): IterableIterator { + return this._icons.values(); + } + + public get onDidRegister(): Event { + return this._onDidRegister.event; + } +} + +const _registry = new Registry(); + +export const iconRegistry: IIconRegistry = _registry; + +export function registerIcon(id: string, def: Codicon) { + return new Codicon(id, def); +} + +export class Codicon { + constructor(public readonly id: string, public readonly definition: Codicon | IconDefinition) { + _registry.add(this); + } + public get classNames() { return 'codicon codicon-' + this.id; } + public get cssSelector() { return '.codicon.codicon-' + this.id; } +} + +interface IconDefinition { + character: string; +} + +export namespace Codicon { + + // built-in icons, with image name + export const add = new Codicon('add', { character: '\\ea60' }); + export const plus = new Codicon('plus', { character: '\\ea60' }); + export const gistNew = new Codicon('gist-new', { character: '\\ea60' }); + export const repoCreate = new Codicon('repo-create', { character: '\\ea60' }); + export const lightbulb = new Codicon('lightbulb', { character: '\\ea61' }); + export const lightBulb = new Codicon('light-bulb', { character: '\\ea61' }); + export const repo = new Codicon('repo', { character: '\\ea62' }); + export const repoDelete = new Codicon('repo-delete', { character: '\\ea62' }); + export const gistFork = new Codicon('gist-fork', { character: '\\ea63' }); + export const repoForked = new Codicon('repo-forked', { character: '\\ea63' }); + export const gitPullRequest = new Codicon('git-pull-request', { character: '\\ea64' }); + export const gitPullRequestAbandoned = new Codicon('git-pull-request-abandoned', { character: '\\ea64' }); + export const recordKeys = new Codicon('record-keys', { character: '\\ea65' }); + export const keyboard = new Codicon('keyboard', { character: '\\ea65' }); + export const tag = new Codicon('tag', { character: '\\ea66' }); + export const tagAdd = new Codicon('tag-add', { character: '\\ea66' }); + export const tagRemove = new Codicon('tag-remove', { character: '\\ea66' }); + export const person = new Codicon('person', { character: '\\ea67' }); + export const personAdd = new Codicon('person-add', { character: '\\ea67' }); + export const personFollow = new Codicon('person-follow', { character: '\\ea67' }); + export const personOutline = new Codicon('person-outline', { character: '\\ea67' }); + export const personFilled = new Codicon('person-filled', { character: '\\ea67' }); + export const gitBranch = new Codicon('git-branch', { character: '\\ea68' }); + export const gitBranchCreate = new Codicon('git-branch-create', { character: '\\ea68' }); + export const gitBranchDelete = new Codicon('git-branch-delete', { character: '\\ea68' }); + export const sourceControl = new Codicon('source-control', { character: '\\ea68' }); + export const mirror = new Codicon('mirror', { character: '\\ea69' }); + export const mirrorPublic = new Codicon('mirror-public', { character: '\\ea69' }); + export const star = new Codicon('star', { character: '\\ea6a' }); + export const starAdd = new Codicon('star-add', { character: '\\ea6a' }); + export const starDelete = new Codicon('star-delete', { character: '\\ea6a' }); + export const starEmpty = new Codicon('star-empty', { character: '\\ea6a' }); + export const comment = new Codicon('comment', { character: '\\ea6b' }); + export const commentAdd = new Codicon('comment-add', { character: '\\ea6b' }); + export const alert = new Codicon('alert', { character: '\\ea6c' }); + export const warning = new Codicon('warning', { character: '\\ea6c' }); + export const search = new Codicon('search', { character: '\\ea6d' }); + export const searchSave = new Codicon('search-save', { character: '\\ea6d' }); + export const logOut = new Codicon('log-out', { character: '\\ea6e' }); + export const signOut = new Codicon('sign-out', { character: '\\ea6e' }); + export const logIn = new Codicon('log-in', { character: '\\ea6f' }); + export const signIn = new Codicon('sign-in', { character: '\\ea6f' }); + export const eye = new Codicon('eye', { character: '\\ea70' }); + export const eyeUnwatch = new Codicon('eye-unwatch', { character: '\\ea70' }); + export const eyeWatch = new Codicon('eye-watch', { character: '\\ea70' }); + export const circleFilled = new Codicon('circle-filled', { character: '\\ea71' }); + export const primitiveDot = new Codicon('primitive-dot', { character: '\\ea71' }); + export const closeDirty = new Codicon('close-dirty', { character: '\\ea71' }); + export const debugBreakpoint = new Codicon('debug-breakpoint', { character: '\\ea71' }); + export const debugBreakpointDisabled = new Codicon('debug-breakpoint-disabled', { character: '\\ea71' }); + export const debugHint = new Codicon('debug-hint', { character: '\\ea71' }); + export const primitiveSquare = new Codicon('primitive-square', { character: '\\ea72' }); + export const edit = new Codicon('edit', { character: '\\ea73' }); + export const pencil = new Codicon('pencil', { character: '\\ea73' }); + export const info = new Codicon('info', { character: '\\ea74' }); + export const issueOpened = new Codicon('issue-opened', { character: '\\ea74' }); + export const gistPrivate = new Codicon('gist-private', { character: '\\ea75' }); + export const gitForkPrivate = new Codicon('git-fork-private', { character: '\\ea75' }); + export const lock = new Codicon('lock', { character: '\\ea75' }); + export const mirrorPrivate = new Codicon('mirror-private', { character: '\\ea75' }); + export const close = new Codicon('close', { character: '\\ea76' }); + export const removeClose = new Codicon('remove-close', { character: '\\ea76' }); + export const x = new Codicon('x', { character: '\\ea76' }); + export const repoSync = new Codicon('repo-sync', { character: '\\ea77' }); + export const sync = new Codicon('sync', { character: '\\ea77' }); + export const clone = new Codicon('clone', { character: '\\ea78' }); + export const desktopDownload = new Codicon('desktop-download', { character: '\\ea78' }); + export const beaker = new Codicon('beaker', { character: '\\ea79' }); + export const microscope = new Codicon('microscope', { character: '\\ea79' }); + export const vm = new Codicon('vm', { character: '\\ea7a' }); + export const deviceDesktop = new Codicon('device-desktop', { character: '\\ea7a' }); + export const file = new Codicon('file', { character: '\\ea7b' }); + export const fileText = new Codicon('file-text', { character: '\\ea7b' }); + export const more = new Codicon('more', { character: '\\ea7c' }); + export const ellipsis = new Codicon('ellipsis', { character: '\\ea7c' }); + export const kebabHorizontal = new Codicon('kebab-horizontal', { character: '\\ea7c' }); + export const mailReply = new Codicon('mail-reply', { character: '\\ea7d' }); + export const reply = new Codicon('reply', { character: '\\ea7d' }); + export const organization = new Codicon('organization', { character: '\\ea7e' }); + export const organizationFilled = new Codicon('organization-filled', { character: '\\ea7e' }); + export const organizationOutline = new Codicon('organization-outline', { character: '\\ea7e' }); + export const newFile = new Codicon('new-file', { character: '\\ea7f' }); + export const fileAdd = new Codicon('file-add', { character: '\\ea7f' }); + export const newFolder = new Codicon('new-folder', { character: '\\ea80' }); + export const fileDirectoryCreate = new Codicon('file-directory-create', { character: '\\ea80' }); + export const trash = new Codicon('trash', { character: '\\ea81' }); + export const trashcan = new Codicon('trashcan', { character: '\\ea81' }); + export const history = new Codicon('history', { character: '\\ea82' }); + export const clock = new Codicon('clock', { character: '\\ea82' }); + export const folder = new Codicon('folder', { character: '\\ea83' }); + export const fileDirectory = new Codicon('file-directory', { character: '\\ea83' }); + export const symbolFolder = new Codicon('symbol-folder', { character: '\\ea83' }); + export const logoGithub = new Codicon('logo-github', { character: '\\ea84' }); + export const markGithub = new Codicon('mark-github', { character: '\\ea84' }); + export const github = new Codicon('github', { character: '\\ea84' }); + export const terminal = new Codicon('terminal', { character: '\\ea85' }); + export const console = new Codicon('console', { character: '\\ea85' }); + export const repl = new Codicon('repl', { character: '\\ea85' }); + export const zap = new Codicon('zap', { character: '\\ea86' }); + export const symbolEvent = new Codicon('symbol-event', { character: '\\ea86' }); + export const error = new Codicon('error', { character: '\\ea87' }); + export const stop = new Codicon('stop', { character: '\\ea87' }); + export const variable = new Codicon('variable', { character: '\\ea88' }); + export const symbolVariable = new Codicon('symbol-variable', { character: '\\ea88' }); + export const array = new Codicon('array', { character: '\\ea8a' }); + export const symbolArray = new Codicon('symbol-array', { character: '\\ea8a' }); + export const symbolModule = new Codicon('symbol-module', { character: '\\ea8b' }); + export const symbolPackage = new Codicon('symbol-package', { character: '\\ea8b' }); + export const symbolNamespace = new Codicon('symbol-namespace', { character: '\\ea8b' }); + export const symbolObject = new Codicon('symbol-object', { character: '\\ea8b' }); + export const symbolMethod = new Codicon('symbol-method', { character: '\\ea8c' }); + export const symbolFunction = new Codicon('symbol-function', { character: '\\ea8c' }); + export const symbolConstructor = new Codicon('symbol-constructor', { character: '\\ea8c' }); + export const symbolBoolean = new Codicon('symbol-boolean', { character: '\\ea8f' }); + export const symbolNull = new Codicon('symbol-null', { character: '\\ea8f' }); + export const symbolNumeric = new Codicon('symbol-numeric', { character: '\\ea90' }); + export const symbolNumber = new Codicon('symbol-number', { character: '\\ea90' }); + export const symbolStructure = new Codicon('symbol-structure', { character: '\\ea91' }); + export const symbolStruct = new Codicon('symbol-struct', { character: '\\ea91' }); + export const symbolParameter = new Codicon('symbol-parameter', { character: '\\ea92' }); + export const symbolTypeParameter = new Codicon('symbol-type-parameter', { character: '\\ea92' }); + export const symbolKey = new Codicon('symbol-key', { character: '\\ea93' }); + export const symbolText = new Codicon('symbol-text', { character: '\\ea93' }); + export const symbolReference = new Codicon('symbol-reference', { character: '\\ea94' }); + export const goToFile = new Codicon('go-to-file', { character: '\\ea94' }); + export const symbolEnum = new Codicon('symbol-enum', { character: '\\ea95' }); + export const symbolValue = new Codicon('symbol-value', { character: '\\ea95' }); + export const symbolRuler = new Codicon('symbol-ruler', { character: '\\ea96' }); + export const symbolUnit = new Codicon('symbol-unit', { character: '\\ea96' }); + export const activateBreakpoints = new Codicon('activate-breakpoints', { character: '\\ea97' }); + export const archive = new Codicon('archive', { character: '\\ea98' }); + export const arrowBoth = new Codicon('arrow-both', { character: '\\ea99' }); + export const arrowDown = new Codicon('arrow-down', { character: '\\ea9a' }); + export const arrowLeft = new Codicon('arrow-left', { character: '\\ea9b' }); + export const arrowRight = new Codicon('arrow-right', { character: '\\ea9c' }); + export const arrowSmallDown = new Codicon('arrow-small-down', { character: '\\ea9d' }); + export const arrowSmallLeft = new Codicon('arrow-small-left', { character: '\\ea9e' }); + export const arrowSmallRight = new Codicon('arrow-small-right', { character: '\\ea9f' }); + export const arrowSmallUp = new Codicon('arrow-small-up', { character: '\\eaa0' }); + export const arrowUp = new Codicon('arrow-up', { character: '\\eaa1' }); + export const bell = new Codicon('bell', { character: '\\eaa2' }); + export const bold = new Codicon('bold', { character: '\\eaa3' }); + export const book = new Codicon('book', { character: '\\eaa4' }); + export const bookmark = new Codicon('bookmark', { character: '\\eaa5' }); + export const debugBreakpointConditionalUnverified = new Codicon('debug-breakpoint-conditional-unverified', { character: '\\eaa6' }); + export const debugBreakpointConditional = new Codicon('debug-breakpoint-conditional', { character: '\\eaa7' }); + export const debugBreakpointConditionalDisabled = new Codicon('debug-breakpoint-conditional-disabled', { character: '\\eaa7' }); + export const debugBreakpointDataUnverified = new Codicon('debug-breakpoint-data-unverified', { character: '\\eaa8' }); + export const debugBreakpointData = new Codicon('debug-breakpoint-data', { character: '\\eaa9' }); + export const debugBreakpointDataDisabled = new Codicon('debug-breakpoint-data-disabled', { character: '\\eaa9' }); + export const debugBreakpointLogUnverified = new Codicon('debug-breakpoint-log-unverified', { character: '\\eaaa' }); + export const debugBreakpointLog = new Codicon('debug-breakpoint-log', { character: '\\eaab' }); + export const debugBreakpointLogDisabled = new Codicon('debug-breakpoint-log-disabled', { character: '\\eaab' }); + export const briefcase = new Codicon('briefcase', { character: '\\eaac' }); + export const broadcast = new Codicon('broadcast', { character: '\\eaad' }); + export const browser = new Codicon('browser', { character: '\\eaae' }); + export const bug = new Codicon('bug', { character: '\\eaaf' }); + export const calendar = new Codicon('calendar', { character: '\\eab0' }); + export const caseSensitive = new Codicon('case-sensitive', { character: '\\eab1' }); + export const check = new Codicon('check', { character: '\\eab2' }); + export const checklist = new Codicon('checklist', { character: '\\eab3' }); + export const chevronDown = new Codicon('chevron-down', { character: '\\eab4' }); + export const chevronLeft = new Codicon('chevron-left', { character: '\\eab5' }); + export const chevronRight = new Codicon('chevron-right', { character: '\\eab6' }); + export const chevronUp = new Codicon('chevron-up', { character: '\\eab7' }); + export const chromeClose = new Codicon('chrome-close', { character: '\\eab8' }); + export const chromeMaximize = new Codicon('chrome-maximize', { character: '\\eab9' }); + export const chromeMinimize = new Codicon('chrome-minimize', { character: '\\eaba' }); + export const chromeRestore = new Codicon('chrome-restore', { character: '\\eabb' }); + export const circleOutline = new Codicon('circle-outline', { character: '\\eabc' }); + export const debugBreakpointUnverified = new Codicon('debug-breakpoint-unverified', { character: '\\eabc' }); + export const circleSlash = new Codicon('circle-slash', { character: '\\eabd' }); + export const circuitBoard = new Codicon('circuit-board', { character: '\\eabe' }); + export const clearAll = new Codicon('clear-all', { character: '\\eabf' }); + export const clippy = new Codicon('clippy', { character: '\\eac0' }); + export const closeAll = new Codicon('close-all', { character: '\\eac1' }); + export const cloudDownload = new Codicon('cloud-download', { character: '\\eac2' }); + export const cloudUpload = new Codicon('cloud-upload', { character: '\\eac3' }); + export const code = new Codicon('code', { character: '\\eac4' }); + export const collapseAll = new Codicon('collapse-all', { character: '\\eac5' }); + export const colorMode = new Codicon('color-mode', { character: '\\eac6' }); + export const commentDiscussion = new Codicon('comment-discussion', { character: '\\eac7' }); + export const compareChanges = new Codicon('compare-changes', { character: '\\eac8' }); + export const creditCard = new Codicon('credit-card', { character: '\\eac9' }); + export const dash = new Codicon('dash', { character: '\\eacc' }); + export const dashboard = new Codicon('dashboard', { character: '\\eacd' }); + export const database = new Codicon('database', { character: '\\eace' }); + export const debugContinue = new Codicon('debug-continue', { character: '\\eacf' }); + export const debugDisconnect = new Codicon('debug-disconnect', { character: '\\ead0' }); + export const debugPause = new Codicon('debug-pause', { character: '\\ead1' }); + export const debugRestart = new Codicon('debug-restart', { character: '\\ead2' }); + export const debugStart = new Codicon('debug-start', { character: '\\ead3' }); + export const debugStepInto = new Codicon('debug-step-into', { character: '\\ead4' }); + export const debugStepOut = new Codicon('debug-step-out', { character: '\\ead5' }); + export const debugStepOver = new Codicon('debug-step-over', { character: '\\ead6' }); + export const debugStop = new Codicon('debug-stop', { character: '\\ead7' }); + export const debug = new Codicon('debug', { character: '\\ead8' }); + export const deviceCameraVideo = new Codicon('device-camera-video', { character: '\\ead9' }); + export const deviceCamera = new Codicon('device-camera', { character: '\\eada' }); + export const deviceMobile = new Codicon('device-mobile', { character: '\\eadb' }); + export const diffAdded = new Codicon('diff-added', { character: '\\eadc' }); + export const diffIgnored = new Codicon('diff-ignored', { character: '\\eadd' }); + export const diffModified = new Codicon('diff-modified', { character: '\\eade' }); + export const diffRemoved = new Codicon('diff-removed', { character: '\\eadf' }); + export const diffRenamed = new Codicon('diff-renamed', { character: '\\eae0' }); + export const diff = new Codicon('diff', { character: '\\eae1' }); + export const discard = new Codicon('discard', { character: '\\eae2' }); + export const editorLayout = new Codicon('editor-layout', { character: '\\eae3' }); + export const emptyWindow = new Codicon('empty-window', { character: '\\eae4' }); + export const exclude = new Codicon('exclude', { character: '\\eae5' }); + export const extensions = new Codicon('extensions', { character: '\\eae6' }); + export const eyeClosed = new Codicon('eye-closed', { character: '\\eae7' }); + export const fileBinary = new Codicon('file-binary', { character: '\\eae8' }); + export const fileCode = new Codicon('file-code', { character: '\\eae9' }); + export const fileMedia = new Codicon('file-media', { character: '\\eaea' }); + export const filePdf = new Codicon('file-pdf', { character: '\\eaeb' }); + export const fileSubmodule = new Codicon('file-submodule', { character: '\\eaec' }); + export const fileSymlinkDirectory = new Codicon('file-symlink-directory', { character: '\\eaed' }); + export const fileSymlinkFile = new Codicon('file-symlink-file', { character: '\\eaee' }); + export const fileZip = new Codicon('file-zip', { character: '\\eaef' }); + export const files = new Codicon('files', { character: '\\eaf0' }); + export const filter = new Codicon('filter', { character: '\\eaf1' }); + export const flame = new Codicon('flame', { character: '\\eaf2' }); + export const foldDown = new Codicon('fold-down', { character: '\\eaf3' }); + export const foldUp = new Codicon('fold-up', { character: '\\eaf4' }); + export const fold = new Codicon('fold', { character: '\\eaf5' }); + export const folderActive = new Codicon('folder-active', { character: '\\eaf6' }); + export const folderOpened = new Codicon('folder-opened', { character: '\\eaf7' }); + export const gear = new Codicon('gear', { character: '\\eaf8' }); + export const gift = new Codicon('gift', { character: '\\eaf9' }); + export const gistSecret = new Codicon('gist-secret', { character: '\\eafa' }); + export const gist = new Codicon('gist', { character: '\\eafb' }); + export const gitCommit = new Codicon('git-commit', { character: '\\eafc' }); + export const gitCompare = new Codicon('git-compare', { character: '\\eafd' }); + export const gitMerge = new Codicon('git-merge', { character: '\\eafe' }); + export const githubAction = new Codicon('github-action', { character: '\\eaff' }); + export const githubAlt = new Codicon('github-alt', { character: '\\eb00' }); + export const globe = new Codicon('globe', { character: '\\eb01' }); + export const grabber = new Codicon('grabber', { character: '\\eb02' }); + export const graph = new Codicon('graph', { character: '\\eb03' }); + export const gripper = new Codicon('gripper', { character: '\\eb04' }); + export const heart = new Codicon('heart', { character: '\\eb05' }); + export const home = new Codicon('home', { character: '\\eb06' }); + export const horizontalRule = new Codicon('horizontal-rule', { character: '\\eb07' }); + export const hubot = new Codicon('hubot', { character: '\\eb08' }); + export const inbox = new Codicon('inbox', { character: '\\eb09' }); + export const issueClosed = new Codicon('issue-closed', { character: '\\eb0a' }); + export const issueReopened = new Codicon('issue-reopened', { character: '\\eb0b' }); + export const issues = new Codicon('issues', { character: '\\eb0c' }); + export const italic = new Codicon('italic', { character: '\\eb0d' }); + export const jersey = new Codicon('jersey', { character: '\\eb0e' }); + export const json = new Codicon('json', { character: '\\eb0f' }); + export const kebabVertical = new Codicon('kebab-vertical', { character: '\\eb10' }); + export const key = new Codicon('key', { character: '\\eb11' }); + export const law = new Codicon('law', { character: '\\eb12' }); + export const lightbulbAutofix = new Codicon('lightbulb-autofix', { character: '\\eb13' }); + export const linkExternal = new Codicon('link-external', { character: '\\eb14' }); + export const link = new Codicon('link', { character: '\\eb15' }); + export const listOrdered = new Codicon('list-ordered', { character: '\\eb16' }); + export const listUnordered = new Codicon('list-unordered', { character: '\\eb17' }); + export const liveShare = new Codicon('live-share', { character: '\\eb18' }); + export const loading = new Codicon('loading', { character: '\\eb19' }); + export const location = new Codicon('location', { character: '\\eb1a' }); + export const mailRead = new Codicon('mail-read', { character: '\\eb1b' }); + export const mail = new Codicon('mail', { character: '\\eb1c' }); + export const markdown = new Codicon('markdown', { character: '\\eb1d' }); + export const megaphone = new Codicon('megaphone', { character: '\\eb1e' }); + export const mention = new Codicon('mention', { character: '\\eb1f' }); + export const milestone = new Codicon('milestone', { character: '\\eb20' }); + export const mortarBoard = new Codicon('mortar-board', { character: '\\eb21' }); + export const move = new Codicon('move', { character: '\\eb22' }); + export const multipleWindows = new Codicon('multiple-windows', { character: '\\eb23' }); + export const mute = new Codicon('mute', { character: '\\eb24' }); + export const noNewline = new Codicon('no-newline', { character: '\\eb25' }); + export const note = new Codicon('note', { character: '\\eb26' }); + export const octoface = new Codicon('octoface', { character: '\\eb27' }); + export const openPreview = new Codicon('open-preview', { character: '\\eb28' }); + export const package_ = new Codicon('package', { character: '\\eb29' }); + export const paintcan = new Codicon('paintcan', { character: '\\eb2a' }); + export const pin = new Codicon('pin', { character: '\\eb2b' }); + export const play = new Codicon('play', { character: '\\eb2c' }); + export const run = new Codicon('run', { character: '\\eb2c' }); + export const plug = new Codicon('plug', { character: '\\eb2d' }); + export const preserveCase = new Codicon('preserve-case', { character: '\\eb2e' }); + export const preview = new Codicon('preview', { character: '\\eb2f' }); + export const project = new Codicon('project', { character: '\\eb30' }); + export const pulse = new Codicon('pulse', { character: '\\eb31' }); + export const question = new Codicon('question', { character: '\\eb32' }); + export const quote = new Codicon('quote', { character: '\\eb33' }); + export const radioTower = new Codicon('radio-tower', { character: '\\eb34' }); + export const reactions = new Codicon('reactions', { character: '\\eb35' }); + export const references = new Codicon('references', { character: '\\eb36' }); + export const refresh = new Codicon('refresh', { character: '\\eb37' }); + export const regex = new Codicon('regex', { character: '\\eb38' }); + export const remoteExplorer = new Codicon('remote-explorer', { character: '\\eb39' }); + export const remote = new Codicon('remote', { character: '\\eb3a' }); + export const remove = new Codicon('remove', { character: '\\eb3b' }); + export const replaceAll = new Codicon('replace-all', { character: '\\eb3c' }); + export const replace = new Codicon('replace', { character: '\\eb3d' }); + export const repoClone = new Codicon('repo-clone', { character: '\\eb3e' }); + export const repoForcePush = new Codicon('repo-force-push', { character: '\\eb3f' }); + export const repoPull = new Codicon('repo-pull', { character: '\\eb40' }); + export const repoPush = new Codicon('repo-push', { character: '\\eb41' }); + export const report = new Codicon('report', { character: '\\eb42' }); + export const requestChanges = new Codicon('request-changes', { character: '\\eb43' }); + export const rocket = new Codicon('rocket', { character: '\\eb44' }); + export const rootFolderOpened = new Codicon('root-folder-opened', { character: '\\eb45' }); + export const rootFolder = new Codicon('root-folder', { character: '\\eb46' }); + export const rss = new Codicon('rss', { character: '\\eb47' }); + export const ruby = new Codicon('ruby', { character: '\\eb48' }); + export const saveAll = new Codicon('save-all', { character: '\\eb49' }); + export const saveAs = new Codicon('save-as', { character: '\\eb4a' }); + export const save = new Codicon('save', { character: '\\eb4b' }); + export const screenFull = new Codicon('screen-full', { character: '\\eb4c' }); + export const screenNormal = new Codicon('screen-normal', { character: '\\eb4d' }); + export const searchStop = new Codicon('search-stop', { character: '\\eb4e' }); + export const server = new Codicon('server', { character: '\\eb50' }); + export const settingsGear = new Codicon('settings-gear', { character: '\\eb51' }); + export const settings = new Codicon('settings', { character: '\\eb52' }); + export const shield = new Codicon('shield', { character: '\\eb53' }); + export const smiley = new Codicon('smiley', { character: '\\eb54' }); + export const sortPrecedence = new Codicon('sort-precedence', { character: '\\eb55' }); + export const splitHorizontal = new Codicon('split-horizontal', { character: '\\eb56' }); + export const splitVertical = new Codicon('split-vertical', { character: '\\eb57' }); + export const squirrel = new Codicon('squirrel', { character: '\\eb58' }); + export const starFull = new Codicon('star-full', { character: '\\eb59' }); + export const starHalf = new Codicon('star-half', { character: '\\eb5a' }); + export const symbolClass = new Codicon('symbol-class', { character: '\\eb5b' }); + export const symbolColor = new Codicon('symbol-color', { character: '\\eb5c' }); + export const symbolConstant = new Codicon('symbol-constant', { character: '\\eb5d' }); + export const symbolEnumMember = new Codicon('symbol-enum-member', { character: '\\eb5e' }); + export const symbolField = new Codicon('symbol-field', { character: '\\eb5f' }); + export const symbolFile = new Codicon('symbol-file', { character: '\\eb60' }); + export const symbolInterface = new Codicon('symbol-interface', { character: '\\eb61' }); + export const symbolKeyword = new Codicon('symbol-keyword', { character: '\\eb62' }); + export const symbolMisc = new Codicon('symbol-misc', { character: '\\eb63' }); + export const symbolOperator = new Codicon('symbol-operator', { character: '\\eb64' }); + export const symbolProperty = new Codicon('symbol-property', { character: '\\eb65' }); + export const wrench = new Codicon('wrench', { character: '\\eb65' }); + export const wrenchSubaction = new Codicon('wrench-subaction', { character: '\\eb65' }); + export const symbolSnippet = new Codicon('symbol-snippet', { character: '\\eb66' }); + export const tasklist = new Codicon('tasklist', { character: '\\eb67' }); + export const telescope = new Codicon('telescope', { character: '\\eb68' }); + export const textSize = new Codicon('text-size', { character: '\\eb69' }); + export const threeBars = new Codicon('three-bars', { character: '\\eb6a' }); + export const thumbsdown = new Codicon('thumbsdown', { character: '\\eb6b' }); + export const thumbsup = new Codicon('thumbsup', { character: '\\eb6c' }); + export const tools = new Codicon('tools', { character: '\\eb6d' }); + export const triangleDown = new Codicon('triangle-down', { character: '\\eb6e' }); + export const triangleLeft = new Codicon('triangle-left', { character: '\\eb6f' }); + export const triangleRight = new Codicon('triangle-right', { character: '\\eb70' }); + export const triangleUp = new Codicon('triangle-up', { character: '\\eb71' }); + export const twitter = new Codicon('twitter', { character: '\\eb72' }); + export const unfold = new Codicon('unfold', { character: '\\eb73' }); + export const unlock = new Codicon('unlock', { character: '\\eb74' }); + export const unmute = new Codicon('unmute', { character: '\\eb75' }); + export const unverified = new Codicon('unverified', { character: '\\eb76' }); + export const verified = new Codicon('verified', { character: '\\eb77' }); + export const versions = new Codicon('versions', { character: '\\eb78' }); + export const vmActive = new Codicon('vm-active', { character: '\\eb79' }); + export const vmOutline = new Codicon('vm-outline', { character: '\\eb7a' }); + export const vmRunning = new Codicon('vm-running', { character: '\\eb7b' }); + export const watch = new Codicon('watch', { character: '\\eb7c' }); + export const whitespace = new Codicon('whitespace', { character: '\\eb7d' }); + export const wholeWord = new Codicon('whole-word', { character: '\\eb7e' }); + export const window = new Codicon('window', { character: '\\eb7f' }); + export const wordWrap = new Codicon('word-wrap', { character: '\\eb80' }); + export const zoomIn = new Codicon('zoom-in', { character: '\\eb81' }); + export const zoomOut = new Codicon('zoom-out', { character: '\\eb82' }); + export const listFilter = new Codicon('list-filter', { character: '\\eb83' }); + export const listFlat = new Codicon('list-flat', { character: '\\eb84' }); + export const listSelection = new Codicon('list-selection', { character: '\\eb85' }); + export const selection = new Codicon('selection', { character: '\\eb85' }); + export const listTree = new Codicon('list-tree', { character: '\\eb86' }); + export const debugBreakpointFunctionUnverified = new Codicon('debug-breakpoint-function-unverified', { character: '\\eb87' }); + export const debugBreakpointFunction = new Codicon('debug-breakpoint-function', { character: '\\eb88' }); + export const debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { character: '\\eb88' }); + export const debugStackframeActive = new Codicon('debug-stackframe-active', { character: '\\eb89' }); + export const debugStackframeDot = new Codicon('debug-stackframe-dot', { character: '\\eb8a' }); + export const debugStackframe = new Codicon('debug-stackframe', { character: '\\eb8b' }); + export const debugStackframeFocused = new Codicon('debug-stackframe-focused', { character: '\\eb8b' }); + export const debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { character: '\\eb8c' }); + export const symbolString = new Codicon('symbol-string', { character: '\\eb8d' }); + export const debugReverseContinue = new Codicon('debug-reverse-continue', { character: '\\eb8e' }); + export const debugStepBack = new Codicon('debug-step-back', { character: '\\eb8f' }); + export const debugRestartFrame = new Codicon('debug-restart-frame', { character: '\\eb90' }); + export const debugAlternate = new Codicon('debug-alternate', { character: '\\eb91' }); + export const callIncoming = new Codicon('call-incoming', { character: '\\eb92' }); + export const callOutgoing = new Codicon('call-outgoing', { character: '\\eb93' }); + export const menu = new Codicon('menu', { character: '\\eb94' }); + export const expandAll = new Codicon('expand-all', { character: '\\eb95' }); + export const feedback = new Codicon('feedback', { character: '\\eb96' }); + export const groupByRefType = new Codicon('group-by-ref-type', { character: '\\eb97' }); + export const ungroupByRefType = new Codicon('ungroup-by-ref-type', { character: '\\eb98' }); + export const account = new Codicon('account', { character: '\\eb99' }); + export const bellDot = new Codicon('bell-dot', { character: '\\eb9a' }); + export const debugConsole = new Codicon('debug-console', { character: '\\eb9b' }); + export const library = new Codicon('library', { character: '\\eb9c' }); + export const output = new Codicon('output', { character: '\\eb9d' }); + export const runAll = new Codicon('run-all', { character: '\\eb9e' }); + export const syncIgnored = new Codicon('sync-ignored', { character: '\\eb9f' }); + export const pinned = new Codicon('pinned', { character: '\\eba0' }); + export const githubInverted = new Codicon('github-inverted', { character: '\\eba1' }); + export const debugAlt2 = new Codicon('debug-alt-2', { character: '\\f101' }); + export const debugAlt = new Codicon('debug-alt', { character: '\\f102' }); +} + + + const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi; export function escapeCodicons(text: string): string { diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 053601db86..df41047755 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -404,10 +404,10 @@ export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch for (let pos = wordStart; pos < _maxLen; pos++) { if (matches[matches.length - (pos + 1)] === '1') { const last = res[res.length - 1]; - if (last && last.end === pos) { - last.end = pos + 1; + if (last && last.end === pos + offset) { + last.end = pos + offset + 1; } else { - res.push({ start: pos + offset, end: pos + 1 + offset }); + res.push({ start: pos + offset, end: pos + offset + 1 }); } } } diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 581ffe99f4..b4098400e6 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -161,7 +161,7 @@ function doScoreFuzzy(query: string, queryLower: string, queryLength: number, ta function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { let score = 0; - if (queryLowerCharAtIndex !== targetLower[targetIndex]) { + if (!considerAsEqual(queryLowerCharAtIndex, targetLower[targetIndex])) { return score; // no match of characters } @@ -228,6 +228,19 @@ function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: strin return score; } +function considerAsEqual(a: string, b: string): boolean { + if (a === b) { + return true; + } + + // Special case path spearators: ignore platform differences + if (a === '/' || a === '\\') { + return b === '/' || b === '\\'; + } + + return false; +} + function scoreSeparatorAtPos(charCode: number): number { switch (charCode) { case CharCode.Slash: @@ -264,15 +277,16 @@ function scoreSeparatorAtPos(charCode: number): number { //#region Alternate fuzzy scorer implementation that is e.g. used for symbols -export type FuzzyScore2 = [number /* score*/, IMatch[]]; +export type FuzzyScore2 = [number | undefined /* score */, IMatch[]]; -const NO_SCORE2: FuzzyScore2 = [NO_MATCH, []]; +const NO_SCORE2: FuzzyScore2 = [undefined, []]; -export function scoreFuzzy2(target: string, query: IPreparedQuery, patternStart = 0, matchOffset = 0): FuzzyScore2 { +export function scoreFuzzy2(target: string, query: IPreparedQuery | IPreparedQueryPiece, patternStart = 0, matchOffset = 0): FuzzyScore2 { // Score: multiple inputs - if (query.values && query.values.length > 1) { - return doScoreFuzzy2Multiple(target, query.values, patternStart, matchOffset); + const preparedQuery = query as IPreparedQuery; + if (preparedQuery.values && preparedQuery.values.length > 1) { + return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, matchOffset); } // Score: single input @@ -285,7 +299,7 @@ function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], pat for (const queryPiece of query) { const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, matchOffset); - if (!score) { + if (typeof score !== 'number') { // if a single query value does not match, return with // no score entirely, we require all queries to match return NO_SCORE2; @@ -796,9 +810,14 @@ export interface IPreparedQueryPiece { export interface IPreparedQuery extends IPreparedQueryPiece { - // Split by spaces + /** + * Query split by spaces into pieces. + */ values: IPreparedQueryPiece[] | undefined; + /** + * Wether the query contains path separator(s) or not. + */ containsPathSeparator: boolean; } diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 5c5b2e5b7f..5c80fde2e0 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -6,7 +6,7 @@ export namespace Iterable { const _empty: Iterable = Object.freeze([]); - export function empty(): Iterable { + export function empty(): Iterable { return _empty; } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index c3de99fbda..d346935064 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -5,7 +5,9 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; -import { compareIgnoreCase, compare } from 'vs/base/common/strings'; +import { compareSubstringIgnoreCase, compare, compareSubstring } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import { isLinux } from 'vs/base/common/platform'; /** * @deprecated ES6: use `[...SetOrMap.values()]` @@ -102,7 +104,10 @@ export class PathIterator implements IKeyIterator { private _from!: number; private _to!: number; - constructor(private _splitOnBackslash: boolean = true) { } + constructor( + private readonly _splitOnBackslash: boolean = true, + private readonly _caseSensitive: boolean = true + ) { } reset(key: string): this { this._value = key.replace(/\\$|\/$/, ''); @@ -135,27 +140,9 @@ export class PathIterator implements IKeyIterator { } cmp(a: string): number { - - let aPos = 0; - const aLen = a.length; - let thisPos = this._from; - - while (aPos < aLen && thisPos < this._to) { - const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); - if (cmp !== 0) { - return cmp; - } - aPos += 1; - thisPos += 1; - } - - if (aLen === this._to - this._from) { - return 0; - } else if (aPos < aLen) { - return -1; - } else { - return 1; - } + return this._caseSensitive + ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) + : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to); } value(): string { @@ -169,7 +156,7 @@ const enum UriIteratorState { export class UriIterator implements IKeyIterator { - private _pathIterator = new PathIterator(false); + private _pathIterator!: PathIterator; private _value!: URI; private _states: UriIteratorState[] = []; private _stateIdx: number = 0; @@ -184,6 +171,10 @@ export class UriIterator implements IKeyIterator { this._states.push(UriIteratorState.Authority); } if (this._value.path) { + //todo@jrieken the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase` + // which cannot be used because it depends on this + const caseSensitive = key.scheme === Schemas.file && isLinux; + this._pathIterator = new PathIterator(false, caseSensitive); this._pathIterator.reset(key.path); if (this._pathIterator.value()) { this._states.push(UriIteratorState.Path); @@ -215,9 +206,9 @@ export class UriIterator implements IKeyIterator { cmp(a: string): number { if (this._states[this._stateIdx] === UriIteratorState.Scheme) { - return compareIgnoreCase(a, this._value.scheme); + return compareSubstringIgnoreCase(a, this._value.scheme); } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { - return compareIgnoreCase(a, this._value.authority); + return compareSubstringIgnoreCase(a, this._value.authority); } else if (this._states[this._stateIdx] === UriIteratorState.Path) { return this._pathIterator.cmp(a); } else if (this._states[this._stateIdx] === UriIteratorState.Query) { diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index 1fba04f8b3..346a1ed5af 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -18,3 +18,19 @@ export class Counter { return this._next++; } } + +export class MovingAverage { + + private _n = 1; + private _val = 0; + + update(value: number): this { + this._val = this._val + (value - this._val) / this._n; + this._n += 1; + return this; + } + + get value(): number { + return this._val; + } +} diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 2d8d49823a..33117bfa71 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -317,7 +317,7 @@ export namespace DataUri { export class ResourceGlobMatcher { private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); + private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forUris<{ root: URI, expression: ParsedExpression }>(); constructor( globalExpression: IExpression, @@ -325,12 +325,12 @@ export class ResourceGlobMatcher { ) { this.globalExpression = parse(globalExpression); for (const expression of rootExpressions) { - this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) }); + this.expressionsByRoot.set(expression.root, { root: expression.root, expression: parse(expression.expression) }); } } matches(resource: URI): boolean { - const rootExpression = this.expressionsByRoot.findSubstr(resource.toString()); + const rootExpression = this.expressionsByRoot.findSubstr(resource); if (rootExpression) { const path = relativePath(rootExpression.root, resource); if (path && !!rootExpression.expression(path)) { diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index b3596e4aef..e0f93e78b8 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -102,14 +102,14 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { ); } - public withScrollDimensions(update: INewScrollDimensions): ScrollState { + public withScrollDimensions(update: INewScrollDimensions, useRawScrollPositions: boolean): ScrollState { return new ScrollState( (typeof update.width !== 'undefined' ? update.width : this.width), (typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth), - this.rawScrollLeft, + useRawScrollPositions ? this.rawScrollLeft : this.scrollLeft, (typeof update.height !== 'undefined' ? update.height : this.height), (typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight), - this.rawScrollTop + useRawScrollPositions ? this.rawScrollTop : this.scrollTop ); } @@ -224,8 +224,8 @@ export class Scrollable extends Disposable { return this._state; } - public setScrollDimensions(dimensions: INewScrollDimensions): void { - const newState = this._state.withScrollDimensions(dimensions); + public setScrollDimensions(dimensions: INewScrollDimensions, useRawScrollPositions: boolean): void { + const newState = this._state.withScrollDimensions(dimensions, useRawScrollPositions); this._setState(newState); // Validate outstanding animated scroll position target diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index fddcae44ad..e3d30cd1ac 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -295,7 +295,31 @@ export function compare(a: string, b: string): number { } } -export function compareIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { +export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { + for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { + let codeA = a.charCodeAt(aStart); + let codeB = b.charCodeAt(bStart); + if (codeA < codeB) { + return -1; + } else if (codeA > codeB) { + return 1; + } + } + const aLen = aEnd - aStart; + const bLen = bEnd - bStart; + if (aLen < bLen) { + return -1; + } else if (aLen > bLen) { + return 1; + } + return 0; +} + +export function compareIgnoreCase(a: string, b: string): number { + return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length); +} + +export function compareSubstringIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { @@ -307,26 +331,20 @@ export function compareIgnoreCase(a: string, b: string, aStart: number = 0, aEnd continue; } - if (isUpperAsciiLetter(codeA)) { - codeA += 32; - } - - if (isUpperAsciiLetter(codeB)) { - codeB += 32; - } - const diff = codeA - codeB; - - if (diff === 0) { - // equal -> ignoreCase + if (diff === 32 && isUpperAsciiLetter(codeB)) { //codeB =[65-90] && codeA =[97-122] continue; - } else if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) { + } else if (diff === -32 && isUpperAsciiLetter(codeA)) { //codeB =[97-122] && codeA =[65-90] + continue; + } + + if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) { // return diff; } else { - return compare(a.toLowerCase(), b.toLowerCase()); + return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd); } } @@ -833,6 +851,9 @@ export function safeBtoa(str: string): string { return btoa(encodeURIComponent(str)); // we use encodeURIComponent because btoa fails for non Latin 1 values } +/** + * @deprecated ES6 + */ export function repeat(s: string, count: number): string { let result = ''; for (let i = 0; i < count; i++) { diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 4a42c9645b..78e61a6e86 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -258,3 +258,19 @@ export type Dto = { [K in keyof T]: T[K] extends URI : T[K] extends Function ? never : UriDto }; + + +export function NotImplementedProxy(name: string): { new(): T } { + return class { + constructor() { + return new Proxy({}, { + get(target: any, prop: PropertyKey) { + if (target[prop]) { + return target[prop]; + } + throw new Error(`Not Implemented: ${name}->${String(prop)}`); + } + }); + } + }; +} diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 732bc0770f..2f0073711a 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -138,10 +138,6 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions }); } -export function decode(buffer: Buffer, encoding: string): string { - return iconv.decode(buffer, toNodeEncoding(encoding)); -} - export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } @@ -154,7 +150,7 @@ export function encodeStream(encoding: string, options?: { addBOM?: boolean }): return iconv.encodeStream(toNodeEncoding(encoding), options); } -function toNodeEncoding(enc: string | null): string { +export function toNodeEncoding(enc: string | null): string { if (enc === UTF8_with_bom || enc === null) { return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it } diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index bf01cc9722..aa2cb9758d 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -109,8 +109,11 @@ .quick-input-action .monaco-text-button { font-size: 85%; - padding: 7px 6px 5.5px 6px; + padding: 0 6px; line-height: initial; + display: flex; + height: 100%; + align-items: center; } .quick-input-message { @@ -202,7 +205,6 @@ } .quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon[class*='codicon-'] { - color: currentColor !important; vertical-align: sub; } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 8da38a7c6d..0c47a5b378 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -27,6 +27,7 @@ import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/lis import { List, IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; import { Color } from 'vs/base/common/color'; +import { registerIcon, Codicon } from 'vs/base/common/codicons'; export interface IQuickInputOptions { idPrefix: string; @@ -67,8 +68,11 @@ const $ = dom.$; type Writeable = { -readonly [P in keyof T]: T[P] }; + +const backButtonIcon = registerIcon('quick-input-back', Codicon.arrowLeft); + const backButton = { - iconClass: 'codicon-arrow-left', + iconClass: backButtonIcon.classNames, tooltip: localize('quickInput.back', "Back"), handle: -1 // TODO }; @@ -662,14 +666,14 @@ class QuickPick extends QuickInput implements IQuickPi this.ui.list.clearFocus(); } })); - this.visibleDisposables.add(this.ui.inputBox.onKeyDown(event => { + this.visibleDisposables.add((this._hideInput ? this.ui.list : this.ui.inputBox).onKeyDown((event: KeyboardEvent | StandardKeyboardEvent) => { switch (event.keyCode) { case KeyCode.DownArrow: this.ui.list.focus(QuickInputListFocus.Next); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.UpArrow: if (this.ui.list.getFocusedElements().length) { @@ -680,21 +684,21 @@ class QuickPick extends QuickInput implements IQuickPi if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.PageDown: this.ui.list.focus(QuickInputListFocus.NextPage); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.PageUp: this.ui.list.focus(QuickInputListFocus.PreviousPage); if (this.canSelectMany) { this.ui.list.domFocus(); } - event.preventDefault(); + dom.EventHelper.stop(event, true); break; case KeyCode.RightArrow: if (!this._canAcceptInBackground) { @@ -711,6 +715,18 @@ class QuickPick extends QuickInput implements IQuickPi this.onDidAcceptEmitter.fire({ inBackground: true }); } + break; + case KeyCode.Home: + if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) { + this.ui.list.focus(QuickInputListFocus.First); + dom.EventHelper.stop(event, true); + } + break; + case KeyCode.End: + if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) { + this.ui.list.focus(QuickInputListFocus.Last); + dom.EventHelper.stop(event, true); + } break; } })); @@ -1077,10 +1093,10 @@ export class QuickInputController extends Disposable { private parentElement: HTMLElement; private styles: IQuickInputStyles; - private onShowEmitter = new Emitter(); + private onShowEmitter = this._register(new Emitter()); readonly onShow = this.onShowEmitter.event; - private onHideEmitter = new Emitter(); + private onHideEmitter = this._register(new Emitter()); readonly onHide = this.onHideEmitter.event; private previousFocusElement?: HTMLElement; @@ -1287,9 +1303,10 @@ export class QuickInputController extends Disposable { return this.ui; } - pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { - return new Promise((doResolve, reject) => { - let resolve = (result: any) => { + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { + type R = (O extends { canPickMany: true } ? T[] : T) | undefined; + return new Promise((doResolve, reject) => { + let resolve = (result: R) => { resolve = doResolve; if (options.onKeyMods) { options.onKeyMods(input.keyMods); @@ -1306,12 +1323,12 @@ export class QuickInputController extends Disposable { input, input.onDidAccept(() => { if (input.canSelectMany) { - resolve(input.selectedItems.slice()); + resolve(input.selectedItems.slice()); input.hide(); } else { const result = input.activeItems[0]; if (result) { - resolve(result); + resolve(result); input.hide(); } } @@ -1326,7 +1343,7 @@ export class QuickInputController extends Disposable { if (!input.canSelectMany) { const result = items[0]; if (result) { - resolve(result); + resolve(result); input.hide(); } } @@ -1388,7 +1405,7 @@ export class QuickInputController extends Disposable { }); } - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { return new Promise((resolve, reject) => { if (token.isCancellationRequested) { resolve(undefined); diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index ed179d6690..f48bc1d7b0 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -25,8 +25,9 @@ import { Action } from 'vs/base/common/actions'; import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; -import { IListOptions, List, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IListOptions, List, IListStyles, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -45,7 +46,7 @@ interface IListElement { readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; } -class ListElement implements IListElement { +class ListElement implements IListElement, IDisposable { index!: number; item!: IQuickPickItem; saneLabel!: string; @@ -74,6 +75,10 @@ class ListElement implements IListElement { constructor(init: IListElement) { assign(this, init); } + + dispose() { + this._onChecked.dispose(); + } } interface IListElementTemplateData { @@ -259,6 +264,8 @@ export class QuickInputList { onChangedCheckedElements: Event = this._onChangedCheckedElements.event; private readonly _onButtonTriggered = new Emitter>(); onButtonTriggered = this._onButtonTriggered.event; + private readonly _onKeyDown = new Emitter(); + onKeyDown: Event = this._onKeyDown.event; private readonly _onLeave = new Emitter(); onLeave: Event = this._onLeave.event; private _fireCheckedEvents = true; @@ -280,13 +287,7 @@ export class QuickInputList { setRowLineHeight: false, multipleSelectionSupport: false, horizontalScrolling: false, - accessibilityProvider, - ariaProvider: { - getRole: () => 'option', - getSetSize: (_: ListElement, _index: number, listLength: number) => listLength, - getPosInSet: (_: ListElement, index: number) => index - }, - ariaRole: 'listbox' + accessibilityProvider } as IListOptions); this.list.getHTMLElement().id = id; this.disposables.push(this.list); @@ -314,6 +315,8 @@ export class QuickInputList { } break; } + + this._onKeyDown.fire(event); })); this.disposables.push(this.list.onMouseDown(e => { if (e.browserEvent.button !== 2) { @@ -341,6 +344,15 @@ export class QuickInputList { this.list.setSelection([e.index]); } })); + this.disposables.push( + this._onChangedAllVisibleChecked, + this._onChangedCheckedCount, + this._onChangedVisibleCount, + this._onChangedCheckedElements, + this._onButtonTriggered, + this._onLeave, + this._onKeyDown + ); } @memoize @@ -439,6 +451,7 @@ export class QuickInputList { } return result; }, [] as ListElement[]); + this.elementDisposables.push(...this.elements); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); this.elementsToIndexes = this.elements.reduce((map, element, index) => { @@ -699,8 +712,21 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor); } -class QuickInputAccessibilityProvider implements IAccessibilityProvider { +class QuickInputAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('quickInput', "Quick Input"); + } + getAriaLabel(element: ListElement): string | null { return element.saneAriaLabel; } + + getWidgetRole() { + return 'listbox'; + } + + getRole() { + return 'option'; + } } diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index b386809100..f12a1ed8b1 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as scorer from 'vs/base/common/fuzzyScorer'; import { URI } from 'vs/base/common/uri'; -import { basename, dirname, sep } from 'vs/base/common/path'; +import { basename, dirname, sep, posix, win32 } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; @@ -27,6 +27,40 @@ class ResourceAccessorClass implements scorer.IItemAccessor { const ResourceAccessor = new ResourceAccessorClass(); +class ResourceWithSlashAccessorClass implements scorer.IItemAccessor { + + getItemLabel(resource: URI): string { + return basename(resource.fsPath); + } + + getItemDescription(resource: URI): string { + return posix.normalize(dirname(resource.path)); + } + + getItemPath(resource: URI): string { + return posix.normalize(resource.path); + } +} + +const ResourceWithSlashAccessor = new ResourceWithSlashAccessorClass(); + +class ResourceWithBackslashAccessorClass implements scorer.IItemAccessor { + + getItemLabel(resource: URI): string { + return basename(resource.fsPath); + } + + getItemDescription(resource: URI): string { + return win32.normalize(dirname(resource.path)); + } + + getItemPath(resource: URI): string { + return win32.normalize(resource.path); + } +} + +const ResourceWithBackslashAccessor = new ResourceWithBackslashAccessorClass(); + class NullAccessorClass implements scorer.IItemAccessor { getItemLabel(resource: URI): string { @@ -48,29 +82,24 @@ function _doScore(target: string, query: string, fuzzy: boolean): scorer.FuzzySc return scorer.scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy); } -function _doScore2(target: string, query: string): scorer.FuzzyScore2 { +function _doScore2(target: string, query: string, matchOffset: number = 0): scorer.FuzzyScore2 { const preparedQuery = scorer.prepareQuery(query); - return scorer.scoreFuzzy2(target, preparedQuery); + return scorer.scoreFuzzy2(target, preparedQuery, 0, matchOffset); } -function scoreItem(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.FuzzyScorerCache): scorer.IItemScore { - return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, cache); +function scoreItem(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor): scorer.IItemScore { + return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null)); } -function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.FuzzyScorerCache): number { - return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache); +function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor): number { + return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, Object.create(null)); } const NullAccessor = new NullAccessorClass(); -let cache: scorer.FuzzyScorerCache = Object.create(null); suite('Fuzzy Scorer', () => { - setup(() => { - cache = Object.create(null); - }); - test('score (fuzzy)', function () { const target = 'HeLlo-World'; @@ -118,16 +147,16 @@ suite('Fuzzy Scorer', () => { }); test('scoreItem - matches are proper', function () { - let res = scoreItem(null, 'something', true, ResourceAccessor, cache); + let res = scoreItem(null, 'something', true, ResourceAccessor); assert.ok(!res.score); const resource = URI.file('/xyz/some/path/someFile123.txt'); - res = scoreItem(resource, 'something', true, NullAccessor, cache); + res = scoreItem(resource, 'something', true, NullAccessor); assert.ok(!res.score); // Path Identity - const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); + const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor); assert.ok(identityRes.score); assert.equal(identityRes.descriptionMatch!.length, 1); assert.equal(identityRes.labelMatch!.length, 1); @@ -137,7 +166,7 @@ suite('Fuzzy Scorer', () => { assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix - const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); + const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); assert.equal(basenamePrefixRes.labelMatch!.length, 1); @@ -145,7 +174,7 @@ suite('Fuzzy Scorer', () => { assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); // Basename Camelcase - const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); + const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); @@ -155,7 +184,7 @@ suite('Fuzzy Scorer', () => { assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); // Basename Match - const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); + const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); assert.equal(basenameRes.labelMatch!.length, 2); @@ -165,7 +194,7 @@ suite('Fuzzy Scorer', () => { assert.equal(basenameRes.labelMatch![1].end, 5); // Path Match - const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -177,7 +206,7 @@ suite('Fuzzy Scorer', () => { assert.equal(pathRes.descriptionMatch![0].end, 4); // No Match - const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); + const noRes = scoreItem(resource, '987', true, ResourceAccessor); assert.ok(!noRes.score); assert.ok(!noRes.labelMatch); assert.ok(!noRes.descriptionMatch); @@ -192,7 +221,7 @@ suite('Fuzzy Scorer', () => { test('scoreItem - multiple', function () { const resource = URI.file('/xyz/some/path/someFile123.txt'); - let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor, cache); + let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor); assert.ok(res1.score); assert.equal(res1.labelMatch?.length, 1); assert.equal(res1.labelMatch![0].start, 0); @@ -201,7 +230,7 @@ suite('Fuzzy Scorer', () => { assert.equal(res1.descriptionMatch![0].start, 1); assert.equal(res1.descriptionMatch![0].end, 4); - let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor, cache); + let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor); assert.ok(res2.score); assert.equal(res1.score, res2.score); assert.equal(res2.labelMatch?.length, 1); @@ -211,7 +240,7 @@ suite('Fuzzy Scorer', () => { assert.equal(res2.descriptionMatch![0].start, 1); assert.equal(res2.descriptionMatch![0].end, 4); - let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor, cache); + let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor); assert.ok(res3.score); assert.ok(res3.score > res2.score); assert.equal(res3.labelMatch?.length, 1); @@ -221,7 +250,7 @@ suite('Fuzzy Scorer', () => { assert.equal(res3.descriptionMatch![0].start, 1); assert.equal(res3.descriptionMatch![0].end, 4); - let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor, cache); + let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor); assert.ok(res4.score); assert.ok(res4.score < res2.score); assert.equal(res4.labelMatch?.length, 0); @@ -234,10 +263,10 @@ suite('Fuzzy Scorer', () => { test('scoreItem - invalid input', function () { - let res = scoreItem(null, null!, true, ResourceAccessor, cache); + let res = scoreItem(null, null!, true, ResourceAccessor); assert.equal(res.score, 0); - res = scoreItem(null, 'null', true, ResourceAccessor, cache); + res = scoreItem(null, 'null', true, ResourceAccessor); assert.equal(res.score, 0); }); @@ -247,7 +276,7 @@ suite('Fuzzy Scorer', () => { // xsp is more relevant to the end of the file path even though it matches // fuzzy also in the beginning. we verify the more relevant match at the // end gets returned. - const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -262,7 +291,7 @@ suite('Fuzzy Scorer', () => { test('scoreItem - avoid match scattering (bug #36119)', function () { const resource = URI.file('projects/ui/cula/ats/target.mk'); - const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -276,7 +305,7 @@ suite('Fuzzy Scorer', () => { // expect "ad" to be matched towards the end of the file because the // match is more compact - const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'ad', true, ResourceAccessor); assert.ok(res.score); assert.ok(res.descriptionMatch); assert.ok(!res.labelMatch!.length); @@ -290,14 +319,14 @@ suite('Fuzzy Scorer', () => { test('scoreItem - proper target offset', function () { const resource = URI.file('etem'); - const res = scoreItem(resource, 'teem', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'teem', true, ResourceAccessor); assert.ok(!res.score); }); test('scoreItem - proper target offset #2', function () { const resource = URI.file('ede'); - const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'de', true, ResourceAccessor); assert.equal(res.labelMatch!.length, 1); assert.equal(res.labelMatch![0].start, 1); @@ -307,7 +336,7 @@ suite('Fuzzy Scorer', () => { test('scoreItem - proper target offset #3', function () { const resource = URI.file('/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg'); - const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'debug', true, ResourceAccessor); assert.equal(res.descriptionMatch!.length, 3); assert.equal(res.descriptionMatch![0].start, 9); @@ -327,7 +356,7 @@ suite('Fuzzy Scorer', () => { test('scoreItem - no match unless query contained in sequence', function () { const resource = URI.file('abcde'); - const res = scoreItem(resource, 'edcda', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'edcda', true, ResourceAccessor); assert.ok(!res.score); }); @@ -336,10 +365,22 @@ suite('Fuzzy Scorer', () => { const remoteResource = URI.from({ scheme: Schemas.vscodeRemote, path: 'abcde/super/duper' }); for (const resource of [localResource, remoteResource]) { - let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor, cache); + let res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceAccessor); assert.ok(res.score); - res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor, cache); + res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithSlashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde\\super\\duper', true, ResourceWithBackslashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithSlashAccessor); + assert.ok(res.score); + + res = scoreItem(resource, 'abcde/super/duper', true, ResourceWithBackslashAccessor); assert.ok(res.score); } }); @@ -352,12 +393,12 @@ suite('Fuzzy Scorer', () => { // Full resource A path let query = ResourceAccessor.getItemPath(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -365,12 +406,12 @@ suite('Fuzzy Scorer', () => { // Full resource B path query = ResourceAccessor.getItemPath(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -384,12 +425,12 @@ suite('Fuzzy Scorer', () => { // Full resource A basename let query = ResourceAccessor.getItemLabel(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -397,12 +438,12 @@ suite('Fuzzy Scorer', () => { // Full resource B basename query = ResourceAccessor.getItemLabel(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -416,12 +457,12 @@ suite('Fuzzy Scorer', () => { // resource A camelcase let query = 'fA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -429,12 +470,12 @@ suite('Fuzzy Scorer', () => { // resource B camelcase query = 'fB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -448,12 +489,12 @@ suite('Fuzzy Scorer', () => { // Resource A part of basename let query = 'fileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -461,12 +502,12 @@ suite('Fuzzy Scorer', () => { // Resource B part of basename query = 'fileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -480,12 +521,12 @@ suite('Fuzzy Scorer', () => { // Resource A part of path let query = 'pathfileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -493,12 +534,12 @@ suite('Fuzzy Scorer', () => { // Resource B part of path query = 'pathfileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -512,12 +553,12 @@ suite('Fuzzy Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -531,12 +572,12 @@ suite('Fuzzy Scorer', () => { // Resource A part of path let query = 'file'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); @@ -550,12 +591,12 @@ suite('Fuzzy Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -568,7 +609,7 @@ suite('Fuzzy Scorer', () => { let query = 'co/te'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -580,7 +621,7 @@ suite('Fuzzy Scorer', () => { let query = 'partsquick'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -591,11 +632,11 @@ suite('Fuzzy Scorer', () => { let query = 'AH'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -606,11 +647,11 @@ suite('Fuzzy Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -621,11 +662,11 @@ suite('Fuzzy Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -636,11 +677,11 @@ suite('Fuzzy Scorer', () => { let query = 'exfile'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -653,18 +694,18 @@ suite('Fuzzy Scorer', () => { let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js'; - let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); query = isWindows ? 'un1\\index.js' : 'un1/index.js'; - res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -675,10 +716,10 @@ suite('Fuzzy Scorer', () => { let query = 'StatVideoindex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); }); @@ -688,10 +729,10 @@ suite('Fuzzy Scorer', () => { let query = 'reproreduxts'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -702,10 +743,10 @@ suite('Fuzzy Scorer', () => { let query = 'bookpageIndex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceC); }); @@ -715,10 +756,10 @@ suite('Fuzzy Scorer', () => { let query = isWindows ? 'ui\\icons' : 'ui/icons'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -728,10 +769,10 @@ suite('Fuzzy Scorer', () => { let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -741,10 +782,10 @@ suite('Fuzzy Scorer', () => { let query = 'djancosig'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -755,12 +796,12 @@ suite('Fuzzy Scorer', () => { let query = 'protectedconfig.php'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); @@ -772,10 +813,10 @@ suite('Fuzzy Scorer', () => { let query = 'gradientmain'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -785,10 +826,10 @@ suite('Fuzzy Scorer', () => { let query = 'abc'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -798,10 +839,10 @@ suite('Fuzzy Scorer', () => { let query = 'xyz'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -811,10 +852,10 @@ suite('Fuzzy Scorer', () => { let query = 'async'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -824,10 +865,10 @@ suite('Fuzzy Scorer', () => { let query = 'partisettings'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -837,10 +878,10 @@ suite('Fuzzy Scorer', () => { let query = 'tipsindex.cshtml'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -850,10 +891,10 @@ suite('Fuzzy Scorer', () => { let query = 'listview'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -864,10 +905,10 @@ suite('Fuzzy Scorer', () => { let query = 'filesexplorerview.ts'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); - res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.equal(res[0], resourceB); }); @@ -932,6 +973,28 @@ suite('Fuzzy Scorer', () => { } }); + test('fuzzyScore2 (matching)', function () { + const target = 'HeLlo-World'; + + for (const offset of [0, 3]) { + let [score, matches] = _doScore2(target, 'HeLlo-World', offset); + + assert.ok(score); + assert.equal(matches.length, 1); + assert.equal(matches[0].start, 0 + offset); + assert.equal(matches[0].end, target.length + offset); + + [score, matches] = _doScore2(target, 'HW', offset); + + assert.ok(score); + assert.equal(matches.length, 2); + assert.equal(matches[0].start, 0 + offset); + assert.equal(matches[0].end, 1 + offset); + assert.equal(matches[1].start, 6 + offset); + assert.equal(matches[1].end, 7 + offset); + } + }); + test('fuzzyScore2 (multiple queries)', function () { const target = 'HeLlo-World'; @@ -957,7 +1020,7 @@ suite('Fuzzy Scorer', () => { } function assertNoScore() { - assert.equal(multiScore, 0); + assert.equal(multiScore, undefined); assert.equal(multiMatches.length, 0); } @@ -975,4 +1038,13 @@ suite('Fuzzy Scorer', () => { [multiScore, multiMatches] = _doScore2(target, 'More Nothing'); assertNoScore(); }); + + test('fuzzyScore2 (#95716)', function () { + const target = '# ❌ Wow'; + + const score = _doScore2(target, '❌'); + assert.ok(score); + assert.ok(typeof score[0] === 'number'); + assert.ok(score[1].length > 0); + }); }); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 683015e3cf..1734e6bcf3 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -84,7 +84,7 @@ suite('Strings', () => { test('compareIgnoreCase (substring)', () => { function assertCompareIgnoreCase(a: string, b: string, aStart: number, aEnd: number, bStart: number, bEnd: number, recurse = true): void { - let actual = strings.compareIgnoreCase(a, b, aStart, aEnd, bStart, bEnd); + let actual = strings.compareSubstringIgnoreCase(a, b, aStart, aEnd, bStart, bEnd); actual = actual > 0 ? 1 : actual < 0 ? -1 : actual; let expected = strings.compare(a.toLowerCase().substring(aStart, aEnd), b.toLowerCase().substring(bStart, bEnd)); diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index fc8a713aa6..44e78401b3 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -8,6 +8,7 @@ import * as fs from 'fs'; import * as encoding from 'vs/base/node/encoding'; import * as terminalEncoding from 'vs/base/node/terminalEncoding'; import { Readable } from 'stream'; +import * as iconv from 'iconv-lite'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export async function detectEncodingByBOM(file: string): Promise { @@ -224,7 +225,7 @@ suite('Encoding', () => { if (err) { reject(err); } else { - resolve(encoding.decode(data, fileEncoding!)); + resolve(iconv.decode(data, encoding.toNodeEncoding(fileEncoding!))); } }); }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 95bd9061c2..7df6fbd3bc 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -8,7 +8,8 @@ import * as os from 'os'; import * as browser from 'vs/base/browser/browser'; import { $ } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; -import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import * as collections from 'vs/base/common/collections'; import { debounce } from 'vs/base/common/decorators'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -715,7 +716,7 @@ export class IssueReporter extends Disposable { type IssueReporterSearchError = { message: string; }; - this.telemetryService.publicLog2('issueReporterSearchError', { message: error.message }); + this.telemetryService.publicLog2('issueReporterSearchError', { message: error.message }, true); } private setUpTypes(): void { diff --git a/src/vs/code/electron-browser/proxy/auth.html b/src/vs/code/electron-browser/proxy/auth.html index d02876abb9..5ef195878c 100644 --- a/src/vs/code/electron-browser/proxy/auth.html +++ b/src/vs/code/electron-browser/proxy/auth.html @@ -4,6 +4,8 @@ +